]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/nss_tacplus/nss_tacplus.c
Merge llvm-project release/17.x llvmorg-17.0.2-0-gb2417f51dbbd
[FreeBSD/FreeBSD.git] / lib / nss_tacplus / nss_tacplus.c
1 /*-
2  * Copyright (c) 2023 Klara, Inc.
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  */
6
7 #include <sys/param.h>
8 #include <sys/limits.h>
9
10 #include <errno.h>
11 #include <inttypes.h>
12 #include <pthread.h>
13 #include <pthread_np.h>
14 #include <stdarg.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <syslog.h>
18
19 #include <nsswitch.h>
20 #include <pwd.h>
21 #include <taclib.h>
22
23 extern int __isthreaded;
24
25 #define DEF_UID         65534
26 #define DEF_GID         65534
27 #define DEF_CLASS       ""
28 #define DEF_DIR         "/"
29 #define DEF_SHELL       "/bin/sh"
30
31 ns_mtab *nss_module_register(const char *, unsigned int *,
32     nss_module_unregister_fn *);
33
34 static void
35 tacplus_error(struct tac_handle *h, const char *func)
36 {
37         if (h == NULL)
38                 syslog(LOG_ERR, "%s(): %m", func);
39         else
40                 syslog(LOG_ERR, "%s(): %s", func, tac_strerror(h));
41 }
42
43 static pthread_key_t tacplus_key;
44
45 static void
46 tacplus_fini(void *p)
47 {
48         struct tac_handle **h = p;
49
50         tac_close(*h);
51         free(h);
52 }
53
54 static void
55 tacplus_keyinit(void)
56 {
57         (void)pthread_key_create(&tacplus_key, tacplus_fini);
58 }
59
60 static struct tac_handle *
61 tacplus_get_handle(void)
62 {
63         static pthread_once_t keyinit = PTHREAD_ONCE_INIT;
64         static struct tac_handle *sth;
65         struct tac_handle **h = &sth;
66         int ret;
67
68         if (__isthreaded && !pthread_main_np()) {
69                 if ((ret = pthread_once(&keyinit, tacplus_keyinit)) != 0)
70                         return (NULL);
71                 if ((h = pthread_getspecific(tacplus_key)) == NULL) {
72                         if ((h = calloc(1, sizeof(*h))) == NULL)
73                                 return (NULL);
74                         if ((pthread_setspecific(tacplus_key, h)) != 0) {
75                                 free(h);
76                                 return (NULL);
77                         }
78                 }
79         }
80         if (*h == NULL) {
81                 if ((*h = tac_open()) == NULL) {
82                         tacplus_error(*h, "tac_open");
83                         return (NULL);
84                 }
85                 if (tac_config(*h, NULL) != 0) {
86                         tacplus_error(*h, "tac_config");
87                         tac_close(*h);
88                         *h = NULL;
89                         return (NULL);
90                 }
91         }
92         return (*h);
93 }
94
95 static char *
96 tacplus_copystr(const char *str, char **buffer, size_t *bufsize)
97 {
98         char *copy = *buffer;
99         size_t len = strlen(str) + 1;
100
101         if (len > *bufsize) {
102                 errno = ERANGE;
103                 return (NULL);
104         }
105         memcpy(copy, str, len);
106         *buffer += len;
107         *bufsize -= len;
108         return (copy);
109 }
110
111 static int
112 tacplus_getpwnam_r(const char *name, struct passwd *pwd, char *buffer,
113     size_t bufsize)
114 {
115         struct tac_handle *h;
116         char *av, *key, *value, *end;
117         intmax_t num;
118         int i, ret;
119
120         if ((h = tacplus_get_handle()) == NULL)
121                 return (NS_UNAVAIL);
122         ret = tac_create_author(h, TAC_AUTHEN_METH_NOT_SET,
123             TAC_AUTHEN_TYPE_NOT_SET, TAC_AUTHEN_SVC_LOGIN);
124         if (ret < 0) {
125                 tacplus_error(h, "tac_create_author");
126                 return (NS_TRYAGAIN);
127         }
128         if (tac_set_user(h, name) < 0) {
129                 tacplus_error(h, "tac_set_user");
130                 return (NS_TRYAGAIN);
131         }
132         if (tac_set_av(h, 0, "service=shell") < 0) {
133                 tacplus_error(h, "tac_set_av");
134                 return (NS_TRYAGAIN);
135         }
136         ret = tac_send_author(h);
137         switch (TAC_AUTHOR_STATUS(ret)) {
138         case TAC_AUTHOR_STATUS_PASS_ADD:
139         case TAC_AUTHOR_STATUS_PASS_REPL:
140                 /* found */
141                 break;
142         case TAC_AUTHOR_STATUS_FAIL:
143                 return (NS_NOTFOUND);
144         case TAC_AUTHOR_STATUS_ERROR:
145                 return (NS_UNAVAIL);
146         default:
147                 tacplus_error(h, "tac_send_author");
148                 return (NS_UNAVAIL);
149         }
150         memset(pwd, 0, sizeof(*pwd));
151
152         /* copy name */
153         pwd->pw_name = tacplus_copystr(name, &buffer, &bufsize);
154         if (pwd->pw_name == NULL)
155                 return (NS_RETURN);
156
157         /* no password */
158         pwd->pw_passwd = tacplus_copystr("*", &buffer, &bufsize);
159         if (2 > bufsize)
160                 return (NS_RETURN);
161
162         /* default uid and gid */
163         pwd->pw_uid = DEF_UID;
164         pwd->pw_gid = DEF_GID;
165
166         /* get attribute-value pairs from TACACS+ response */
167         for (i = 0; i < TAC_AUTHEN_AV_COUNT(ret); i++) {
168                 if ((av = tac_get_av(h, i)) == NULL) {
169                         tacplus_error(h, "tac_get_av");
170                         return (NS_UNAVAIL);
171                 }
172                 key = av;
173                 if ((value = strchr(av, '=')) == NULL) {
174                         free(av);
175                         return (NS_RETURN);
176                 }
177                 *value++ = '\0';
178                 if (strcasecmp(key, "uid") == 0) {
179                         num = strtoimax(value, &end, 10);
180                         if (end == value || *end != '\0' ||
181                             num < 0 || num > (intmax_t)UID_MAX) {
182                                 errno = EINVAL;
183                                 free(av);
184                                 return (NS_RETURN);
185                         }
186                         pwd->pw_uid = num;
187                 } else if (strcasecmp(key, "gid") == 0) {
188                         num = strtoimax(value, &end, 10);
189                         if (end == value || *end != '\0' ||
190                             num < 0 || num > (intmax_t)GID_MAX) {
191                                 errno = EINVAL;
192                                 free(av);
193                                 return (NS_RETURN);
194                         }
195                         pwd->pw_gid = num;
196                 } else if (strcasecmp(av, "class") == 0) {
197                         pwd->pw_class = tacplus_copystr(value, &buffer,
198                             &bufsize);
199                         if (pwd->pw_class == NULL) {
200                                 free(av);
201                                 return (NS_RETURN);
202                         }
203                 } else if (strcasecmp(av, "gecos") == 0) {
204                         pwd->pw_gecos = tacplus_copystr(value, &buffer,
205                             &bufsize);
206                         if (pwd->pw_gecos == NULL) {
207                                 free(av);
208                                 return (NS_RETURN);
209                         }
210                 } else if (strcasecmp(av, "home") == 0) {
211                         pwd->pw_dir = tacplus_copystr(value, &buffer,
212                             &bufsize);
213                         if (pwd->pw_dir == NULL) {
214                                 free(av);
215                                 return (NS_RETURN);
216                         }
217                 } else if (strcasecmp(av, "shell") == 0) {
218                         pwd->pw_shell = tacplus_copystr(value, &buffer,
219                             &bufsize);
220                         if (pwd->pw_shell == NULL) {
221                                 free(av);
222                                 return (NS_RETURN);
223                         }
224                 }
225                 free(av);
226         }
227
228         /* default class if none was provided */
229         if (pwd->pw_class == NULL)
230                 pwd->pw_class = tacplus_copystr(DEF_CLASS, &buffer, &bufsize);
231
232         /* gecos equal to name if none was provided */
233         if (pwd->pw_gecos == NULL)
234                 pwd->pw_gecos = pwd->pw_name;
235
236         /* default home directory if none was provided */
237         if (pwd->pw_dir == NULL)
238                 pwd->pw_dir = tacplus_copystr(DEF_DIR, &buffer, &bufsize);
239         if (pwd->pw_dir == NULL)
240                 return (NS_RETURN);
241
242         /* default shell if none was provided */
243         if (pwd->pw_shell == NULL)
244                 pwd->pw_shell = tacplus_copystr(DEF_SHELL, &buffer, &bufsize);
245         if (pwd->pw_shell == NULL)
246                 return (NS_RETURN);
247
248         /* done! */
249         return (NS_SUCCESS);
250 }
251
252 static int
253 nss_tacplus_getpwnam_r(void *retval, void *mdata __unused, va_list ap)
254 {
255         char *name = va_arg(ap, char *);
256         struct passwd *pwd = va_arg(ap, struct passwd *);
257         char *buffer = va_arg(ap, char *);
258         size_t bufsize = va_arg(ap, size_t);
259         int *result = va_arg(ap, int *);
260         int ret;
261
262         errno = 0;
263         ret = tacplus_getpwnam_r(name, pwd, buffer, bufsize);
264         if (ret == NS_SUCCESS) {
265                 *(void **)retval = pwd;
266                 *result = 0;
267         } else {
268                 *(void **)retval = NULL;
269                 *result = errno;
270         }
271         return (ret);
272 }
273
274 static int
275 nss_tacplus_setpwent(void *retval __unused, void *mdata __unused,
276     va_list ap __unused)
277 {
278         return (NS_SUCCESS);
279 }
280
281 static int
282 nss_tacplus_getpwent_r(void *retval, void *mdata __unused, va_list ap)
283 {
284         struct passwd *pwd __unused = va_arg(ap, struct passwd *);
285         char *buffer __unused = va_arg(ap, char *);
286         size_t bufsize __unused = va_arg(ap, size_t);
287         int *result = va_arg(ap, int *);
288
289         *(void **)retval = NULL;
290         *result = 0;
291         return (NS_SUCCESS);
292
293 }
294
295 static int
296 nss_tacplus_endpwent(void *retval __unused, void *mdata __unused,
297     va_list ap __unused)
298 {
299         return (NS_SUCCESS);
300 }
301
302 ns_mtab *
303 nss_module_register(const char *name __unused, unsigned int *plen,
304     nss_module_unregister_fn *unreg)
305 {
306         static ns_mtab mtab[] = {
307                 { "passwd", "getpwnam_r", &nss_tacplus_getpwnam_r, NULL },
308                 { "passwd", "setpwent", &nss_tacplus_setpwent, NULL },
309                 { "passwd", "getpwent_r", &nss_tacplus_getpwent_r, NULL },
310                 { "passwd", "endpwent", &nss_tacplus_endpwent, NULL },
311         };
312
313         *plen = nitems(mtab);
314         *unreg = NULL;
315         return (mtab);
316 }