Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F145410592
D40133.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
9 KB
Referenced Files
None
Subscribers
None
D40133.diff
View Options
diff --git a/lib/Makefile b/lib/Makefile
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -109,7 +109,8 @@
liby \
libz \
libzstd \
- ncurses
+ ncurses \
+ nss_tacplus
# Inter-library dependencies. When the makefile for a library contains LDADD
# libraries, those libraries should be listed as build order dependencies here.
@@ -145,6 +146,7 @@
.if ${MK_OFED} != "no"
SUBDIR_DEPEND_libpcap= ofed
.endif
+SUBDIR_DEPEND_nss_tacplus= libtacplus
.if !defined(COMPAT_32BIT)
SUBDIR+= flua
diff --git a/lib/nss_tacplus/Makefile b/lib/nss_tacplus/Makefile
new file mode 100644
--- /dev/null
+++ b/lib/nss_tacplus/Makefile
@@ -0,0 +1,9 @@
+LIB= nss_tacplus
+SRCS= ${LIB}.8
+SHLIB_MAJOR= 1
+SHLIB_NAME= ${LIB}.so.${SHLIB_MAJOR}
+LIBADD= tacplus
+MK_INSTALLIB= no
+MAN= ${LIB}.8
+
+.include <bsd.lib.mk>
diff --git a/lib/nss_tacplus/nss_tacplus.8 b/lib/nss_tacplus/nss_tacplus.8
new file mode 100644
--- /dev/null
+++ b/lib/nss_tacplus/nss_tacplus.8
@@ -0,0 +1,86 @@
+.\"-
+.\" Copyright (c) 2023 Klara, Inc.
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.Dd May 17, 2023
+.Dt NSS_TACPLUS 8
+.Os
+.Sh NAME
+.Nm nss_tacplus
+.Nd TACACS+ nsswitch module
+.Sh SYNOPSIS
+.Ic passwd : files tacplus
+.Sh DESCRIPTION
+The
+.Nm
+module is a loadable NSS module which provides a minimal identity
+service using a TACACS+ backend.
+.Pp
+Due to the limitations of the TACACS+ protocol, the functionality
+provided by the
+.Nm
+module is very limited: it can look up a user by name, but not by uid,
+and it cannot enumerate users.
+.Pp
+To look up a user, the
+.Nm
+module submits an authorization request with authentication method
+.Dv TAC_PLUS_AUTHEN_METH_NOT_SET ,
+authentication type
+.Dv TAC_PLUS_AUTHEN_TYPE_NOT_SET ,
+and authentication service
+.Dv TAC_PLUS_AUTHEN_SVC_LOGIN ,
+for the
+.Dq shell
+service.
+If the response status is either
+.Dv TAC_PLUS_AUTHOR_STATUS_PASS_ADD
+or
+.Dv TAC_PLUS_AUTHOR_STATUS_PASS_REPL ,
+the user is considered to exist and the
+.Nm
+module fills out a
+.Vt struct passwd
+for it.
+.Pp
+The following attributes, if included in the response from the TACACS+
+server, are used to construct the response:
+.Bl -tag -width GECOS
+.It Va UID
+Numeric user ID.
+Must be between 0 and
+.Dv UID_MAX .
+Defaults to 65534.
+.It Va GID
+Numeric primary group ID.
+Must be between 0 and
+.Dv GID_MAX .
+Defaults to 65534.
+.It Va GECOS
+Display name.
+If not provided, the user name is used instead.
+.It Va HOME
+Home directory.
+Defaults to
+.Pa / .
+.It Va SHELL
+Shell.
+Defaults to
+.Pa /bin/sh .
+.El
+.Pp
+Case is ignored when matching attribute names.
+If an attribute is included multiple times, the last value takes
+effect.
+.Sh SEE ALSO
+.Xr libtacplus 3 ,
+.Xr tacplus.conf 5 ,
+.Xr pam_tacplus 8
+.Sh HISTORY
+.An -nosplit
+The
+.Nm
+module and this manual page were written by
+.An Dag-Erling Smørgrav Aq Mt des@FreeBSD.org
+for Klara Systems.
diff --git a/lib/nss_tacplus/nss_tacplus.c b/lib/nss_tacplus/nss_tacplus.c
new file mode 100644
--- /dev/null
+++ b/lib/nss_tacplus/nss_tacplus.c
@@ -0,0 +1,273 @@
+/*-
+ * Copyright (c) 2023 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/limits.h>
+
+#include <errno.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <pthread_np.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <nsswitch.h>
+#include <pwd.h>
+#include <taclib.h>
+
+extern int __isthreaded;
+
+#define DEF_UID 65534
+#define DEF_GID 65534
+#define DEF_DIR "/"
+#define DEF_SHELL "/bin/sh"
+
+ns_mtab *nss_module_register(const char *, unsigned int *,
+ nss_module_unregister_fn *);
+
+static void
+tacplus_error(struct tac_handle *h, const char *func)
+{
+ if (h == NULL)
+ syslog(LOG_ERR, "%s(): %m", func);
+ else
+ syslog(LOG_ERR, "%s(): %s", func, tac_strerror(h));
+}
+
+static pthread_key_t tacplus_key;
+
+static void
+tacplus_fini(void *p)
+{
+ struct tac_handle **h = p;
+
+ tac_close(*h);
+ free(h);
+}
+
+static void
+tacplus_keyinit(void)
+{
+ (void)pthread_key_create(&tacplus_key, tacplus_fini);
+}
+
+static struct tac_handle *
+tacplus_get_handle(void)
+{
+ static pthread_once_t keyinit = PTHREAD_ONCE_INIT;
+ static struct tac_handle *sth;
+ struct tac_handle **h = &sth;
+ int ret;
+
+ if (__isthreaded && !pthread_main_np()) {
+ if ((ret = pthread_once(&keyinit, tacplus_keyinit)) != 0)
+ return (NULL);
+ if ((h = pthread_getspecific(tacplus_key)) == NULL) {
+ if ((h = calloc(1, sizeof(*h))) == NULL)
+ return (NULL);
+ if ((pthread_setspecific(tacplus_key, h)) != 0) {
+ free(h);
+ return (NULL);
+ }
+ }
+ }
+ if (*h == NULL) {
+ if ((*h = tac_open()) == NULL) {
+ tacplus_error(*h, "tac_open");
+ return (NULL);
+ }
+ if (tac_config(*h, NULL) != 0) {
+ tacplus_error(*h, "tac_config");
+ tac_close(*h);
+ *h = NULL;
+ return (NULL);
+ }
+ }
+ return (*h);
+}
+
+static char *
+tacplus_copystr(const char *str, char **buffer, size_t *bufsize)
+{
+ char *copy = *buffer;
+ size_t len = strlen(str) + 1;
+
+ if (len > *bufsize) {
+ errno = ERANGE;
+ return (NULL);
+ }
+ memcpy(copy, str, len);
+ *buffer += len;
+ *bufsize -= len;
+ return (copy);
+}
+
+static int
+tacplus_getpwnam_r(const char *name, struct passwd *pwd, char *buffer,
+ size_t bufsize)
+{
+ struct tac_handle *h;
+ char *av, *key, *value, *end;
+ intmax_t num;
+ int i, ret;
+
+ if ((h = tacplus_get_handle()) == NULL)
+ return (NS_UNAVAIL);
+ ret = tac_create_author(h, TAC_AUTHEN_METH_NOT_SET,
+ TAC_AUTHEN_TYPE_NOT_SET, TAC_AUTHEN_SVC_LOGIN);
+ if (ret < 0) {
+ tacplus_error(h, "tac_create_author");
+ return (NS_TRYAGAIN);
+ }
+ if (tac_set_user(h, name) < 0) {
+ tacplus_error(h, "tac_set_user");
+ return (NS_TRYAGAIN);
+ }
+ if (tac_set_av(h, 0, "service=shell") < 0) {
+ tacplus_error(h, "tac_set_av");
+ return (NS_TRYAGAIN);
+ }
+ ret = tac_send_author(h);
+ switch (TAC_AUTHOR_STATUS(ret)) {
+ case TAC_AUTHOR_STATUS_PASS_ADD:
+ case TAC_AUTHOR_STATUS_PASS_REPL:
+ /* found */
+ break;
+ case TAC_AUTHOR_STATUS_FAIL:
+ return (NS_NOTFOUND);
+ case TAC_AUTHOR_STATUS_ERROR:
+ return (NS_UNAVAIL);
+ default:
+ tacplus_error(h, "tac_send_author");
+ return (NS_UNAVAIL);
+ }
+ memset(pwd, 0, sizeof(*pwd));
+
+ /* copy name */
+ pwd->pw_name = tacplus_copystr(name, &buffer, &bufsize);
+ if (pwd->pw_name == NULL)
+ return (NS_RETURN);
+
+ /* no password */
+ pwd->pw_passwd = tacplus_copystr("*", &buffer, &bufsize);
+ if (2 > bufsize)
+ return (NS_RETURN);
+
+ /* default uid and gid */
+ pwd->pw_uid = DEF_UID;
+ pwd->pw_gid = DEF_GID;
+
+ /* get attribute-value pairs from TACACS+ response */
+ for (i = 0; i < TAC_AUTHEN_AV_COUNT(ret); i++) {
+ if ((av = tac_get_av(h, i)) == NULL) {
+ tacplus_error(h, "tac_get_av");
+ return (NS_UNAVAIL);
+ }
+ key = av;
+ if ((value = strchr(av, '=')) == NULL) {
+ free(av);
+ return (NS_RETURN);
+ }
+ *value++ = '\0';
+ if (strcasecmp(key, "uid") == 0) {
+ num = strtoimax(value, &end, 10);
+ if (end == value || *end != '\0' ||
+ num < 0 || num > (intmax_t)UID_MAX) {
+ errno = EINVAL;
+ free(av);
+ return (NS_RETURN);
+ }
+ pwd->pw_uid = num;
+ } else if (strcasecmp(key, "gid") == 0) {
+ num = strtoimax(value, &end, 10);
+ if (end == value || *end != '\0' ||
+ num < 0 || num > (intmax_t)GID_MAX) {
+ errno = EINVAL;
+ free(av);
+ return (NS_RETURN);
+ }
+ pwd->pw_gid = num;
+ } else if (strcasecmp(av, "gecos") == 0) {
+ pwd->pw_gecos = tacplus_copystr(value, &buffer,
+ &bufsize);
+ if (pwd->pw_gecos == NULL) {
+ free(av);
+ return (NS_RETURN);
+ }
+ } else if (strcasecmp(av, "home") == 0) {
+ pwd->pw_dir = tacplus_copystr(value, &buffer,
+ &bufsize);
+ if (pwd->pw_dir == NULL) {
+ free(av);
+ return (NS_RETURN);
+ }
+ } else if (strcasecmp(av, "shell") == 0) {
+ pwd->pw_shell = tacplus_copystr(value, &buffer,
+ &bufsize);
+ if (pwd->pw_shell == NULL) {
+ free(av);
+ return (NS_RETURN);
+ }
+ }
+ free(av);
+ }
+
+ /* gecos equal to name if none was provided */
+ if (pwd->pw_gecos == NULL)
+ pwd->pw_gecos = pwd->pw_name;
+
+ /* default home directory if none was provided */
+ if (pwd->pw_dir == NULL)
+ pwd->pw_dir = tacplus_copystr(DEF_DIR, &buffer, &bufsize);
+ if (pwd->pw_dir == NULL)
+ return (NS_RETURN);
+
+ /* default shell if none was provided */
+ if (pwd->pw_shell == NULL)
+ pwd->pw_shell = tacplus_copystr(DEF_SHELL, &buffer, &bufsize);
+ if (pwd->pw_shell == NULL)
+ return (NS_RETURN);
+
+ /* done! */
+ return (NS_SUCCESS);
+}
+
+static int
+nss_tacplus_getpwnam_r(void *retval, void *mdata __unused, va_list ap)
+{
+ char *name = va_arg(ap, char *);
+ struct passwd *pwd = va_arg(ap, struct passwd *);
+ char *buffer = va_arg(ap, char *);
+ size_t bufsize = va_arg(ap, size_t);
+ int *result = va_arg(ap, int *);
+ int ret;
+
+ errno = 0;
+ ret = tacplus_getpwnam_r(name, pwd, buffer, bufsize);
+ if (ret == NS_SUCCESS) {
+ *(void **)retval = pwd;
+ *result = 0;
+ } else {
+ *(void **)retval = NULL;
+ *result = errno;
+ }
+ return (ret);
+}
+
+ns_mtab *
+nss_module_register(const char *name __unused, unsigned int *plen,
+ nss_module_unregister_fn *unreg)
+{
+ static ns_mtab mtab[] = {
+ { "passwd", "getpwnam_r", &nss_tacplus_getpwnam_r, NULL },
+ };
+
+ *plen = nitems(mtab);
+ *unreg = NULL;
+ return (mtab);
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Feb 20, 12:05 PM (5 h, 1 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28898927
Default Alt Text
D40133.diff (9 KB)
Attached To
Mode
D40133: Add nss_tacplus, a TACACS+ NSS module.
Attached
Detach File
Event Timeline
Log In to Comment