16c5cdba1SDag-Erling Smørgrav /*- 26c5cdba1SDag-Erling Smørgrav * Copyright (c) 2023 Klara, Inc. 36c5cdba1SDag-Erling Smørgrav * 46c5cdba1SDag-Erling Smørgrav * SPDX-License-Identifier: BSD-2-Clause 56c5cdba1SDag-Erling Smørgrav */ 66c5cdba1SDag-Erling Smørgrav 76c5cdba1SDag-Erling Smørgrav #include <sys/param.h> 86c5cdba1SDag-Erling Smørgrav #include <sys/limits.h> 96c5cdba1SDag-Erling Smørgrav 106c5cdba1SDag-Erling Smørgrav #include <errno.h> 116c5cdba1SDag-Erling Smørgrav #include <inttypes.h> 126c5cdba1SDag-Erling Smørgrav #include <pthread.h> 136c5cdba1SDag-Erling Smørgrav #include <pthread_np.h> 146c5cdba1SDag-Erling Smørgrav #include <stdarg.h> 156c5cdba1SDag-Erling Smørgrav #include <stdlib.h> 166c5cdba1SDag-Erling Smørgrav #include <string.h> 176c5cdba1SDag-Erling Smørgrav #include <syslog.h> 186c5cdba1SDag-Erling Smørgrav 196c5cdba1SDag-Erling Smørgrav #include <nsswitch.h> 206c5cdba1SDag-Erling Smørgrav #include <pwd.h> 216c5cdba1SDag-Erling Smørgrav #include <taclib.h> 226c5cdba1SDag-Erling Smørgrav 236c5cdba1SDag-Erling Smørgrav extern int __isthreaded; 246c5cdba1SDag-Erling Smørgrav 256c5cdba1SDag-Erling Smørgrav #define DEF_UID 65534 266c5cdba1SDag-Erling Smørgrav #define DEF_GID 65534 27*246aec9aSDag-Erling Smørgrav #define DEF_CLASS "" 286c5cdba1SDag-Erling Smørgrav #define DEF_DIR "/" 296c5cdba1SDag-Erling Smørgrav #define DEF_SHELL "/bin/sh" 306c5cdba1SDag-Erling Smørgrav 316c5cdba1SDag-Erling Smørgrav ns_mtab *nss_module_register(const char *, unsigned int *, 326c5cdba1SDag-Erling Smørgrav nss_module_unregister_fn *); 336c5cdba1SDag-Erling Smørgrav 346c5cdba1SDag-Erling Smørgrav static void 356c5cdba1SDag-Erling Smørgrav tacplus_error(struct tac_handle *h, const char *func) 366c5cdba1SDag-Erling Smørgrav { 376c5cdba1SDag-Erling Smørgrav if (h == NULL) 386c5cdba1SDag-Erling Smørgrav syslog(LOG_ERR, "%s(): %m", func); 396c5cdba1SDag-Erling Smørgrav else 406c5cdba1SDag-Erling Smørgrav syslog(LOG_ERR, "%s(): %s", func, tac_strerror(h)); 416c5cdba1SDag-Erling Smørgrav } 426c5cdba1SDag-Erling Smørgrav 436c5cdba1SDag-Erling Smørgrav static pthread_key_t tacplus_key; 446c5cdba1SDag-Erling Smørgrav 456c5cdba1SDag-Erling Smørgrav static void 466c5cdba1SDag-Erling Smørgrav tacplus_fini(void *p) 476c5cdba1SDag-Erling Smørgrav { 486c5cdba1SDag-Erling Smørgrav struct tac_handle **h = p; 496c5cdba1SDag-Erling Smørgrav 506c5cdba1SDag-Erling Smørgrav tac_close(*h); 516c5cdba1SDag-Erling Smørgrav free(h); 526c5cdba1SDag-Erling Smørgrav } 536c5cdba1SDag-Erling Smørgrav 546c5cdba1SDag-Erling Smørgrav static void 556c5cdba1SDag-Erling Smørgrav tacplus_keyinit(void) 566c5cdba1SDag-Erling Smørgrav { 576c5cdba1SDag-Erling Smørgrav (void)pthread_key_create(&tacplus_key, tacplus_fini); 586c5cdba1SDag-Erling Smørgrav } 596c5cdba1SDag-Erling Smørgrav 606c5cdba1SDag-Erling Smørgrav static struct tac_handle * 616c5cdba1SDag-Erling Smørgrav tacplus_get_handle(void) 626c5cdba1SDag-Erling Smørgrav { 636c5cdba1SDag-Erling Smørgrav static pthread_once_t keyinit = PTHREAD_ONCE_INIT; 646c5cdba1SDag-Erling Smørgrav static struct tac_handle *sth; 656c5cdba1SDag-Erling Smørgrav struct tac_handle **h = &sth; 666c5cdba1SDag-Erling Smørgrav int ret; 676c5cdba1SDag-Erling Smørgrav 686c5cdba1SDag-Erling Smørgrav if (__isthreaded && !pthread_main_np()) { 696c5cdba1SDag-Erling Smørgrav if ((ret = pthread_once(&keyinit, tacplus_keyinit)) != 0) 706c5cdba1SDag-Erling Smørgrav return (NULL); 716c5cdba1SDag-Erling Smørgrav if ((h = pthread_getspecific(tacplus_key)) == NULL) { 726c5cdba1SDag-Erling Smørgrav if ((h = calloc(1, sizeof(*h))) == NULL) 736c5cdba1SDag-Erling Smørgrav return (NULL); 746c5cdba1SDag-Erling Smørgrav if ((pthread_setspecific(tacplus_key, h)) != 0) { 756c5cdba1SDag-Erling Smørgrav free(h); 766c5cdba1SDag-Erling Smørgrav return (NULL); 776c5cdba1SDag-Erling Smørgrav } 786c5cdba1SDag-Erling Smørgrav } 796c5cdba1SDag-Erling Smørgrav } 806c5cdba1SDag-Erling Smørgrav if (*h == NULL) { 816c5cdba1SDag-Erling Smørgrav if ((*h = tac_open()) == NULL) { 826c5cdba1SDag-Erling Smørgrav tacplus_error(*h, "tac_open"); 836c5cdba1SDag-Erling Smørgrav return (NULL); 846c5cdba1SDag-Erling Smørgrav } 856c5cdba1SDag-Erling Smørgrav if (tac_config(*h, NULL) != 0) { 866c5cdba1SDag-Erling Smørgrav tacplus_error(*h, "tac_config"); 876c5cdba1SDag-Erling Smørgrav tac_close(*h); 886c5cdba1SDag-Erling Smørgrav *h = NULL; 896c5cdba1SDag-Erling Smørgrav return (NULL); 906c5cdba1SDag-Erling Smørgrav } 916c5cdba1SDag-Erling Smørgrav } 926c5cdba1SDag-Erling Smørgrav return (*h); 936c5cdba1SDag-Erling Smørgrav } 946c5cdba1SDag-Erling Smørgrav 956c5cdba1SDag-Erling Smørgrav static char * 966c5cdba1SDag-Erling Smørgrav tacplus_copystr(const char *str, char **buffer, size_t *bufsize) 976c5cdba1SDag-Erling Smørgrav { 986c5cdba1SDag-Erling Smørgrav char *copy = *buffer; 996c5cdba1SDag-Erling Smørgrav size_t len = strlen(str) + 1; 1006c5cdba1SDag-Erling Smørgrav 1016c5cdba1SDag-Erling Smørgrav if (len > *bufsize) { 1026c5cdba1SDag-Erling Smørgrav errno = ERANGE; 1036c5cdba1SDag-Erling Smørgrav return (NULL); 1046c5cdba1SDag-Erling Smørgrav } 1056c5cdba1SDag-Erling Smørgrav memcpy(copy, str, len); 1066c5cdba1SDag-Erling Smørgrav *buffer += len; 1076c5cdba1SDag-Erling Smørgrav *bufsize -= len; 1086c5cdba1SDag-Erling Smørgrav return (copy); 1096c5cdba1SDag-Erling Smørgrav } 1106c5cdba1SDag-Erling Smørgrav 1116c5cdba1SDag-Erling Smørgrav static int 1126c5cdba1SDag-Erling Smørgrav tacplus_getpwnam_r(const char *name, struct passwd *pwd, char *buffer, 1136c5cdba1SDag-Erling Smørgrav size_t bufsize) 1146c5cdba1SDag-Erling Smørgrav { 1156c5cdba1SDag-Erling Smørgrav struct tac_handle *h; 1166c5cdba1SDag-Erling Smørgrav char *av, *key, *value, *end; 1176c5cdba1SDag-Erling Smørgrav intmax_t num; 1186c5cdba1SDag-Erling Smørgrav int i, ret; 1196c5cdba1SDag-Erling Smørgrav 1206c5cdba1SDag-Erling Smørgrav if ((h = tacplus_get_handle()) == NULL) 1216c5cdba1SDag-Erling Smørgrav return (NS_UNAVAIL); 1226c5cdba1SDag-Erling Smørgrav ret = tac_create_author(h, TAC_AUTHEN_METH_NOT_SET, 1236c5cdba1SDag-Erling Smørgrav TAC_AUTHEN_TYPE_NOT_SET, TAC_AUTHEN_SVC_LOGIN); 1246c5cdba1SDag-Erling Smørgrav if (ret < 0) { 1256c5cdba1SDag-Erling Smørgrav tacplus_error(h, "tac_create_author"); 1266c5cdba1SDag-Erling Smørgrav return (NS_TRYAGAIN); 1276c5cdba1SDag-Erling Smørgrav } 1286c5cdba1SDag-Erling Smørgrav if (tac_set_user(h, name) < 0) { 1296c5cdba1SDag-Erling Smørgrav tacplus_error(h, "tac_set_user"); 1306c5cdba1SDag-Erling Smørgrav return (NS_TRYAGAIN); 1316c5cdba1SDag-Erling Smørgrav } 1326c5cdba1SDag-Erling Smørgrav if (tac_set_av(h, 0, "service=shell") < 0) { 1336c5cdba1SDag-Erling Smørgrav tacplus_error(h, "tac_set_av"); 1346c5cdba1SDag-Erling Smørgrav return (NS_TRYAGAIN); 1356c5cdba1SDag-Erling Smørgrav } 1366c5cdba1SDag-Erling Smørgrav ret = tac_send_author(h); 1376c5cdba1SDag-Erling Smørgrav switch (TAC_AUTHOR_STATUS(ret)) { 1386c5cdba1SDag-Erling Smørgrav case TAC_AUTHOR_STATUS_PASS_ADD: 1396c5cdba1SDag-Erling Smørgrav case TAC_AUTHOR_STATUS_PASS_REPL: 1406c5cdba1SDag-Erling Smørgrav /* found */ 1416c5cdba1SDag-Erling Smørgrav break; 1426c5cdba1SDag-Erling Smørgrav case TAC_AUTHOR_STATUS_FAIL: 1436c5cdba1SDag-Erling Smørgrav return (NS_NOTFOUND); 1446c5cdba1SDag-Erling Smørgrav case TAC_AUTHOR_STATUS_ERROR: 1456c5cdba1SDag-Erling Smørgrav return (NS_UNAVAIL); 1466c5cdba1SDag-Erling Smørgrav default: 1476c5cdba1SDag-Erling Smørgrav tacplus_error(h, "tac_send_author"); 1486c5cdba1SDag-Erling Smørgrav return (NS_UNAVAIL); 1496c5cdba1SDag-Erling Smørgrav } 1506c5cdba1SDag-Erling Smørgrav memset(pwd, 0, sizeof(*pwd)); 1516c5cdba1SDag-Erling Smørgrav 1526c5cdba1SDag-Erling Smørgrav /* copy name */ 1536c5cdba1SDag-Erling Smørgrav pwd->pw_name = tacplus_copystr(name, &buffer, &bufsize); 1546c5cdba1SDag-Erling Smørgrav if (pwd->pw_name == NULL) 1556c5cdba1SDag-Erling Smørgrav return (NS_RETURN); 1566c5cdba1SDag-Erling Smørgrav 1576c5cdba1SDag-Erling Smørgrav /* no password */ 1586c5cdba1SDag-Erling Smørgrav pwd->pw_passwd = tacplus_copystr("*", &buffer, &bufsize); 1596c5cdba1SDag-Erling Smørgrav if (2 > bufsize) 1606c5cdba1SDag-Erling Smørgrav return (NS_RETURN); 1616c5cdba1SDag-Erling Smørgrav 1626c5cdba1SDag-Erling Smørgrav /* default uid and gid */ 1636c5cdba1SDag-Erling Smørgrav pwd->pw_uid = DEF_UID; 1646c5cdba1SDag-Erling Smørgrav pwd->pw_gid = DEF_GID; 1656c5cdba1SDag-Erling Smørgrav 1666c5cdba1SDag-Erling Smørgrav /* get attribute-value pairs from TACACS+ response */ 1676c5cdba1SDag-Erling Smørgrav for (i = 0; i < TAC_AUTHEN_AV_COUNT(ret); i++) { 1686c5cdba1SDag-Erling Smørgrav if ((av = tac_get_av(h, i)) == NULL) { 1696c5cdba1SDag-Erling Smørgrav tacplus_error(h, "tac_get_av"); 1706c5cdba1SDag-Erling Smørgrav return (NS_UNAVAIL); 1716c5cdba1SDag-Erling Smørgrav } 1726c5cdba1SDag-Erling Smørgrav key = av; 1736c5cdba1SDag-Erling Smørgrav if ((value = strchr(av, '=')) == NULL) { 1746c5cdba1SDag-Erling Smørgrav free(av); 1756c5cdba1SDag-Erling Smørgrav return (NS_RETURN); 1766c5cdba1SDag-Erling Smørgrav } 1776c5cdba1SDag-Erling Smørgrav *value++ = '\0'; 1786c5cdba1SDag-Erling Smørgrav if (strcasecmp(key, "uid") == 0) { 1796c5cdba1SDag-Erling Smørgrav num = strtoimax(value, &end, 10); 1806c5cdba1SDag-Erling Smørgrav if (end == value || *end != '\0' || 1816c5cdba1SDag-Erling Smørgrav num < 0 || num > (intmax_t)UID_MAX) { 1826c5cdba1SDag-Erling Smørgrav errno = EINVAL; 1836c5cdba1SDag-Erling Smørgrav free(av); 1846c5cdba1SDag-Erling Smørgrav return (NS_RETURN); 1856c5cdba1SDag-Erling Smørgrav } 1866c5cdba1SDag-Erling Smørgrav pwd->pw_uid = num; 1876c5cdba1SDag-Erling Smørgrav } else if (strcasecmp(key, "gid") == 0) { 1886c5cdba1SDag-Erling Smørgrav num = strtoimax(value, &end, 10); 1896c5cdba1SDag-Erling Smørgrav if (end == value || *end != '\0' || 1906c5cdba1SDag-Erling Smørgrav num < 0 || num > (intmax_t)GID_MAX) { 1916c5cdba1SDag-Erling Smørgrav errno = EINVAL; 1926c5cdba1SDag-Erling Smørgrav free(av); 1936c5cdba1SDag-Erling Smørgrav return (NS_RETURN); 1946c5cdba1SDag-Erling Smørgrav } 1956c5cdba1SDag-Erling Smørgrav pwd->pw_gid = num; 196*246aec9aSDag-Erling Smørgrav } else if (strcasecmp(av, "class") == 0) { 197*246aec9aSDag-Erling Smørgrav pwd->pw_class = tacplus_copystr(value, &buffer, 198*246aec9aSDag-Erling Smørgrav &bufsize); 199*246aec9aSDag-Erling Smørgrav if (pwd->pw_class == NULL) { 200*246aec9aSDag-Erling Smørgrav free(av); 201*246aec9aSDag-Erling Smørgrav return (NS_RETURN); 202*246aec9aSDag-Erling Smørgrav } 2036c5cdba1SDag-Erling Smørgrav } else if (strcasecmp(av, "gecos") == 0) { 2046c5cdba1SDag-Erling Smørgrav pwd->pw_gecos = tacplus_copystr(value, &buffer, 2056c5cdba1SDag-Erling Smørgrav &bufsize); 2066c5cdba1SDag-Erling Smørgrav if (pwd->pw_gecos == NULL) { 2076c5cdba1SDag-Erling Smørgrav free(av); 2086c5cdba1SDag-Erling Smørgrav return (NS_RETURN); 2096c5cdba1SDag-Erling Smørgrav } 2106c5cdba1SDag-Erling Smørgrav } else if (strcasecmp(av, "home") == 0) { 2116c5cdba1SDag-Erling Smørgrav pwd->pw_dir = tacplus_copystr(value, &buffer, 2126c5cdba1SDag-Erling Smørgrav &bufsize); 2136c5cdba1SDag-Erling Smørgrav if (pwd->pw_dir == NULL) { 2146c5cdba1SDag-Erling Smørgrav free(av); 2156c5cdba1SDag-Erling Smørgrav return (NS_RETURN); 2166c5cdba1SDag-Erling Smørgrav } 2176c5cdba1SDag-Erling Smørgrav } else if (strcasecmp(av, "shell") == 0) { 2186c5cdba1SDag-Erling Smørgrav pwd->pw_shell = tacplus_copystr(value, &buffer, 2196c5cdba1SDag-Erling Smørgrav &bufsize); 2206c5cdba1SDag-Erling Smørgrav if (pwd->pw_shell == NULL) { 2216c5cdba1SDag-Erling Smørgrav free(av); 2226c5cdba1SDag-Erling Smørgrav return (NS_RETURN); 2236c5cdba1SDag-Erling Smørgrav } 2246c5cdba1SDag-Erling Smørgrav } 2256c5cdba1SDag-Erling Smørgrav free(av); 2266c5cdba1SDag-Erling Smørgrav } 2276c5cdba1SDag-Erling Smørgrav 228*246aec9aSDag-Erling Smørgrav /* default class if none was provided */ 229*246aec9aSDag-Erling Smørgrav if (pwd->pw_class == NULL) 230*246aec9aSDag-Erling Smørgrav pwd->pw_class = tacplus_copystr(DEF_CLASS, &buffer, &bufsize); 231*246aec9aSDag-Erling Smørgrav 2326c5cdba1SDag-Erling Smørgrav /* gecos equal to name if none was provided */ 2336c5cdba1SDag-Erling Smørgrav if (pwd->pw_gecos == NULL) 2346c5cdba1SDag-Erling Smørgrav pwd->pw_gecos = pwd->pw_name; 2356c5cdba1SDag-Erling Smørgrav 2366c5cdba1SDag-Erling Smørgrav /* default home directory if none was provided */ 2376c5cdba1SDag-Erling Smørgrav if (pwd->pw_dir == NULL) 2386c5cdba1SDag-Erling Smørgrav pwd->pw_dir = tacplus_copystr(DEF_DIR, &buffer, &bufsize); 2396c5cdba1SDag-Erling Smørgrav if (pwd->pw_dir == NULL) 2406c5cdba1SDag-Erling Smørgrav return (NS_RETURN); 2416c5cdba1SDag-Erling Smørgrav 2426c5cdba1SDag-Erling Smørgrav /* default shell if none was provided */ 2436c5cdba1SDag-Erling Smørgrav if (pwd->pw_shell == NULL) 2446c5cdba1SDag-Erling Smørgrav pwd->pw_shell = tacplus_copystr(DEF_SHELL, &buffer, &bufsize); 2456c5cdba1SDag-Erling Smørgrav if (pwd->pw_shell == NULL) 2466c5cdba1SDag-Erling Smørgrav return (NS_RETURN); 2476c5cdba1SDag-Erling Smørgrav 2486c5cdba1SDag-Erling Smørgrav /* done! */ 2496c5cdba1SDag-Erling Smørgrav return (NS_SUCCESS); 2506c5cdba1SDag-Erling Smørgrav } 2516c5cdba1SDag-Erling Smørgrav 2526c5cdba1SDag-Erling Smørgrav static int 2536c5cdba1SDag-Erling Smørgrav nss_tacplus_getpwnam_r(void *retval, void *mdata __unused, va_list ap) 2546c5cdba1SDag-Erling Smørgrav { 2556c5cdba1SDag-Erling Smørgrav char *name = va_arg(ap, char *); 2566c5cdba1SDag-Erling Smørgrav struct passwd *pwd = va_arg(ap, struct passwd *); 2576c5cdba1SDag-Erling Smørgrav char *buffer = va_arg(ap, char *); 2586c5cdba1SDag-Erling Smørgrav size_t bufsize = va_arg(ap, size_t); 2596c5cdba1SDag-Erling Smørgrav int *result = va_arg(ap, int *); 2606c5cdba1SDag-Erling Smørgrav int ret; 2616c5cdba1SDag-Erling Smørgrav 2626c5cdba1SDag-Erling Smørgrav errno = 0; 2636c5cdba1SDag-Erling Smørgrav ret = tacplus_getpwnam_r(name, pwd, buffer, bufsize); 2646c5cdba1SDag-Erling Smørgrav if (ret == NS_SUCCESS) { 2656c5cdba1SDag-Erling Smørgrav *(void **)retval = pwd; 2666c5cdba1SDag-Erling Smørgrav *result = 0; 2676c5cdba1SDag-Erling Smørgrav } else { 2686c5cdba1SDag-Erling Smørgrav *(void **)retval = NULL; 2696c5cdba1SDag-Erling Smørgrav *result = errno; 2706c5cdba1SDag-Erling Smørgrav } 2716c5cdba1SDag-Erling Smørgrav return (ret); 2726c5cdba1SDag-Erling Smørgrav } 2736c5cdba1SDag-Erling Smørgrav 2746c5cdba1SDag-Erling Smørgrav ns_mtab * 2756c5cdba1SDag-Erling Smørgrav nss_module_register(const char *name __unused, unsigned int *plen, 2766c5cdba1SDag-Erling Smørgrav nss_module_unregister_fn *unreg) 2776c5cdba1SDag-Erling Smørgrav { 2786c5cdba1SDag-Erling Smørgrav static ns_mtab mtab[] = { 2796c5cdba1SDag-Erling Smørgrav { "passwd", "getpwnam_r", &nss_tacplus_getpwnam_r, NULL }, 2806c5cdba1SDag-Erling Smørgrav }; 2816c5cdba1SDag-Erling Smørgrav 2826c5cdba1SDag-Erling Smørgrav *plen = nitems(mtab); 2836c5cdba1SDag-Erling Smørgrav *unreg = NULL; 2846c5cdba1SDag-Erling Smørgrav return (mtab); 2856c5cdba1SDag-Erling Smørgrav } 286