]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/nss_tacplus/nss_tacplus.c
zfs: merge openzfs/zfs@ad0a55461
[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_DIR         "/"
28 #define DEF_SHELL       "/bin/sh"
29
30 ns_mtab *nss_module_register(const char *, unsigned int *,
31     nss_module_unregister_fn *);
32
33 static void
34 tacplus_error(struct tac_handle *h, const char *func)
35 {
36         if (h == NULL)
37                 syslog(LOG_ERR, "%s(): %m", func);
38         else
39                 syslog(LOG_ERR, "%s(): %s", func, tac_strerror(h));
40 }
41
42 static pthread_key_t tacplus_key;
43
44 static void
45 tacplus_fini(void *p)
46 {
47         struct tac_handle **h = p;
48
49         tac_close(*h);
50         free(h);
51 }
52
53 static void
54 tacplus_keyinit(void)
55 {
56         (void)pthread_key_create(&tacplus_key, tacplus_fini);
57 }
58
59 static struct tac_handle *
60 tacplus_get_handle(void)
61 {
62         static pthread_once_t keyinit = PTHREAD_ONCE_INIT;
63         static struct tac_handle *sth;
64         struct tac_handle **h = &sth;
65         int ret;
66
67         if (__isthreaded && !pthread_main_np()) {
68                 if ((ret = pthread_once(&keyinit, tacplus_keyinit)) != 0)
69                         return (NULL);
70                 if ((h = pthread_getspecific(tacplus_key)) == NULL) {
71                         if ((h = calloc(1, sizeof(*h))) == NULL)
72                                 return (NULL);
73                         if ((pthread_setspecific(tacplus_key, h)) != 0) {
74                                 free(h);
75                                 return (NULL);
76                         }
77                 }
78         }
79         if (*h == NULL) {
80                 if ((*h = tac_open()) == NULL) {
81                         tacplus_error(*h, "tac_open");
82                         return (NULL);
83                 }
84                 if (tac_config(*h, NULL) != 0) {
85                         tacplus_error(*h, "tac_config");
86                         tac_close(*h);
87                         *h = NULL;
88                         return (NULL);
89                 }
90         }
91         return (*h);
92 }
93
94 static char *
95 tacplus_copystr(const char *str, char **buffer, size_t *bufsize)
96 {
97         char *copy = *buffer;
98         size_t len = strlen(str) + 1;
99
100         if (len > *bufsize) {
101                 errno = ERANGE;
102                 return (NULL);
103         }
104         memcpy(copy, str, len);
105         *buffer += len;
106         *bufsize -= len;
107         return (copy);
108 }
109
110 static int
111 tacplus_getpwnam_r(const char *name, struct passwd *pwd, char *buffer,
112     size_t bufsize)
113 {
114         struct tac_handle *h;
115         char *av, *key, *value, *end;
116         intmax_t num;
117         int i, ret;
118
119         if ((h = tacplus_get_handle()) == NULL)
120                 return (NS_UNAVAIL);
121         ret = tac_create_author(h, TAC_AUTHEN_METH_NOT_SET,
122             TAC_AUTHEN_TYPE_NOT_SET, TAC_AUTHEN_SVC_LOGIN);
123         if (ret < 0) {
124                 tacplus_error(h, "tac_create_author");
125                 return (NS_TRYAGAIN);
126         }
127         if (tac_set_user(h, name) < 0) {
128                 tacplus_error(h, "tac_set_user");
129                 return (NS_TRYAGAIN);
130         }
131         if (tac_set_av(h, 0, "service=shell") < 0) {
132                 tacplus_error(h, "tac_set_av");
133                 return (NS_TRYAGAIN);
134         }
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:
139                 /* found */
140                 break;
141         case TAC_AUTHOR_STATUS_FAIL:
142                 return (NS_NOTFOUND);
143         case TAC_AUTHOR_STATUS_ERROR:
144                 return (NS_UNAVAIL);
145         default:
146                 tacplus_error(h, "tac_send_author");
147                 return (NS_UNAVAIL);
148         }
149         memset(pwd, 0, sizeof(*pwd));
150
151         /* copy name */
152         pwd->pw_name = tacplus_copystr(name, &buffer, &bufsize);
153         if (pwd->pw_name == NULL)
154                 return (NS_RETURN);
155
156         /* no password */
157         pwd->pw_passwd = tacplus_copystr("*", &buffer, &bufsize);
158         if (2 > bufsize)
159                 return (NS_RETURN);
160
161         /* default uid and gid */
162         pwd->pw_uid = DEF_UID;
163         pwd->pw_gid = DEF_GID;
164
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");
169                         return (NS_UNAVAIL);
170                 }
171                 key = av;
172                 if ((value = strchr(av, '=')) == NULL) {
173                         free(av);
174                         return (NS_RETURN);
175                 }
176                 *value++ = '\0';
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) {
181                                 errno = EINVAL;
182                                 free(av);
183                                 return (NS_RETURN);
184                         }
185                         pwd->pw_uid = num;
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) {
190                                 errno = EINVAL;
191                                 free(av);
192                                 return (NS_RETURN);
193                         }
194                         pwd->pw_gid = num;
195                 } else if (strcasecmp(av, "gecos") == 0) {
196                         pwd->pw_gecos = tacplus_copystr(value, &buffer,
197                             &bufsize);
198                         if (pwd->pw_gecos == NULL) {
199                                 free(av);
200                                 return (NS_RETURN);
201                         }
202                 } else if (strcasecmp(av, "home") == 0) {
203                         pwd->pw_dir = tacplus_copystr(value, &buffer,
204                             &bufsize);
205                         if (pwd->pw_dir == NULL) {
206                                 free(av);
207                                 return (NS_RETURN);
208                         }
209                 } else if (strcasecmp(av, "shell") == 0) {
210                         pwd->pw_shell = tacplus_copystr(value, &buffer,
211                             &bufsize);
212                         if (pwd->pw_shell == NULL) {
213                                 free(av);
214                                 return (NS_RETURN);
215                         }
216                 }
217                 free(av);
218         }
219
220         /* gecos equal to name if none was provided */
221         if (pwd->pw_gecos == NULL)
222                 pwd->pw_gecos = pwd->pw_name;
223
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)
228                 return (NS_RETURN);
229
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)
234                 return (NS_RETURN);
235
236         /* done! */
237         return (NS_SUCCESS);
238 }
239
240 static int
241 nss_tacplus_getpwnam_r(void *retval, void *mdata __unused, va_list ap)
242 {
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 *);
248         int ret;
249
250         errno = 0;
251         ret = tacplus_getpwnam_r(name, pwd, buffer, bufsize);
252         if (ret == NS_SUCCESS) {
253                 *(void **)retval = pwd;
254                 *result = 0;
255         } else {
256                 *(void **)retval = NULL;
257                 *result = errno;
258         }
259         return (ret);
260 }
261
262 ns_mtab *
263 nss_module_register(const char *name __unused, unsigned int *plen,
264     nss_module_unregister_fn *unreg)
265 {
266         static ns_mtab mtab[] = {
267                 { "passwd", "getpwnam_r", &nss_tacplus_getpwnam_r, NULL },
268         };
269
270         *plen = nitems(mtab);
271         *unreg = NULL;
272         return (mtab);
273 }