2 * Copyright (c) 2023 Klara, Inc.
4 * SPDX-License-Identifier: BSD-2-Clause
8 #include <sys/limits.h>
13 #include <pthread_np.h>
23 extern int __isthreaded;
28 #define DEF_SHELL "/bin/sh"
30 ns_mtab *nss_module_register(const char *, unsigned int *,
31 nss_module_unregister_fn *);
34 tacplus_error(struct tac_handle *h, const char *func)
37 syslog(LOG_ERR, "%s(): %m", func);
39 syslog(LOG_ERR, "%s(): %s", func, tac_strerror(h));
42 static pthread_key_t tacplus_key;
47 struct tac_handle **h = p;
56 (void)pthread_key_create(&tacplus_key, tacplus_fini);
59 static struct tac_handle *
60 tacplus_get_handle(void)
62 static pthread_once_t keyinit = PTHREAD_ONCE_INIT;
63 static struct tac_handle *sth;
64 struct tac_handle **h = &sth;
67 if (__isthreaded && !pthread_main_np()) {
68 if ((ret = pthread_once(&keyinit, tacplus_keyinit)) != 0)
70 if ((h = pthread_getspecific(tacplus_key)) == NULL) {
71 if ((h = calloc(1, sizeof(*h))) == NULL)
73 if ((pthread_setspecific(tacplus_key, h)) != 0) {
80 if ((*h = tac_open()) == NULL) {
81 tacplus_error(*h, "tac_open");
84 if (tac_config(*h, NULL) != 0) {
85 tacplus_error(*h, "tac_config");
95 tacplus_copystr(const char *str, char **buffer, size_t *bufsize)
98 size_t len = strlen(str) + 1;
100 if (len > *bufsize) {
104 memcpy(copy, str, len);
111 tacplus_getpwnam_r(const char *name, struct passwd *pwd, char *buffer,
114 struct tac_handle *h;
115 char *av, *key, *value, *end;
119 if ((h = tacplus_get_handle()) == NULL)
121 ret = tac_create_author(h, TAC_AUTHEN_METH_NOT_SET,
122 TAC_AUTHEN_TYPE_NOT_SET, TAC_AUTHEN_SVC_LOGIN);
124 tacplus_error(h, "tac_create_author");
125 return (NS_TRYAGAIN);
127 if (tac_set_user(h, name) < 0) {
128 tacplus_error(h, "tac_set_user");
129 return (NS_TRYAGAIN);
131 if (tac_set_av(h, 0, "service=shell") < 0) {
132 tacplus_error(h, "tac_set_av");
133 return (NS_TRYAGAIN);
135 ret = tac_send_author(h);
136 switch (TAC_AUTHOR_STATUS(ret)) {
137 case TAC_AUTHOR_STATUS_PASS_ADD:
138 case TAC_AUTHOR_STATUS_PASS_REPL:
141 case TAC_AUTHOR_STATUS_FAIL:
142 return (NS_NOTFOUND);
143 case TAC_AUTHOR_STATUS_ERROR:
146 tacplus_error(h, "tac_send_author");
149 memset(pwd, 0, sizeof(*pwd));
152 pwd->pw_name = tacplus_copystr(name, &buffer, &bufsize);
153 if (pwd->pw_name == NULL)
157 pwd->pw_passwd = tacplus_copystr("*", &buffer, &bufsize);
161 /* default uid and gid */
162 pwd->pw_uid = DEF_UID;
163 pwd->pw_gid = DEF_GID;
165 /* get attribute-value pairs from TACACS+ response */
166 for (i = 0; i < TAC_AUTHEN_AV_COUNT(ret); i++) {
167 if ((av = tac_get_av(h, i)) == NULL) {
168 tacplus_error(h, "tac_get_av");
172 if ((value = strchr(av, '=')) == NULL) {
177 if (strcasecmp(key, "uid") == 0) {
178 num = strtoimax(value, &end, 10);
179 if (end == value || *end != '\0' ||
180 num < 0 || num > (intmax_t)UID_MAX) {
186 } else if (strcasecmp(key, "gid") == 0) {
187 num = strtoimax(value, &end, 10);
188 if (end == value || *end != '\0' ||
189 num < 0 || num > (intmax_t)GID_MAX) {
195 } else if (strcasecmp(av, "gecos") == 0) {
196 pwd->pw_gecos = tacplus_copystr(value, &buffer,
198 if (pwd->pw_gecos == NULL) {
202 } else if (strcasecmp(av, "home") == 0) {
203 pwd->pw_dir = tacplus_copystr(value, &buffer,
205 if (pwd->pw_dir == NULL) {
209 } else if (strcasecmp(av, "shell") == 0) {
210 pwd->pw_shell = tacplus_copystr(value, &buffer,
212 if (pwd->pw_shell == NULL) {
220 /* gecos equal to name if none was provided */
221 if (pwd->pw_gecos == NULL)
222 pwd->pw_gecos = pwd->pw_name;
224 /* default home directory if none was provided */
225 if (pwd->pw_dir == NULL)
226 pwd->pw_dir = tacplus_copystr(DEF_DIR, &buffer, &bufsize);
227 if (pwd->pw_dir == NULL)
230 /* default shell if none was provided */
231 if (pwd->pw_shell == NULL)
232 pwd->pw_shell = tacplus_copystr(DEF_SHELL, &buffer, &bufsize);
233 if (pwd->pw_shell == NULL)
241 nss_tacplus_getpwnam_r(void *retval, void *mdata __unused, va_list ap)
243 char *name = va_arg(ap, char *);
244 struct passwd *pwd = va_arg(ap, struct passwd *);
245 char *buffer = va_arg(ap, char *);
246 size_t bufsize = va_arg(ap, size_t);
247 int *result = va_arg(ap, int *);
251 ret = tacplus_getpwnam_r(name, pwd, buffer, bufsize);
252 if (ret == NS_SUCCESS) {
253 *(void **)retval = pwd;
256 *(void **)retval = NULL;
263 nss_module_register(const char *name __unused, unsigned int *plen,
264 nss_module_unregister_fn *unreg)
266 static ns_mtab mtab[] = {
267 { "passwd", "getpwnam_r", &nss_tacplus_getpwnam_r, NULL },
270 *plen = nitems(mtab);