/* This file is part of pam-modules. Copyright (C) 2018 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifdef HAVE__PAM_ACONF_H #include #endif #include #include #include "graypam.h" #ifndef LINUX_PAM #include #endif #include static long debug_level; char const *host_name; char const *domain_name; char const *netgroup_name; int use_getdomainname; int use_resolve; enum { SENSE_ALLOW, SENSE_DENY }; const char *sense_choice[] = { "allow", "deny", NULL }; static int sense; struct pam_opt pam_opt[] = { { PAM_OPTSTR(debug), pam_opt_long, &debug_level }, { PAM_OPTSTR(debug), pam_opt_const, &debug_level, { 1 } }, { PAM_OPTSTR(audit), pam_opt_const, &debug_level, { 100 } }, { PAM_OPTSTR(waitdebug), pam_opt_null, NULL, { 0 }, gray_wait_debug_fun }, { PAM_OPTSTR(netgroup), pam_opt_string, &netgroup_name }, { PAM_OPTSTR(hostname), pam_opt_string, &host_name }, { PAM_OPTSTR(domainname), pam_opt_string, &domain_name }, { PAM_OPTSTR(getdomainname), pam_opt_bool, &use_getdomainname }, { PAM_OPTSTR(resolve), pam_opt_bool, &use_resolve }, { PAM_OPTSTR(sense), pam_opt_enum, &sense, { enumstr: sense_choice } }, { NULL } }; #ifndef MAXHOSTNAMELEN # define MAXHOSTNAMELEN 64 #endif #ifndef SIZE_T_MAX # define SIZE_T_MAX ((size_t)-1) #endif int xgetname (int (*getfn)(char *, size_t), char **storage) { char *buffer = NULL; size_t size = 0; char *p; while (1) { if (size == 0) { size = MAXHOSTNAMELEN; p = malloc(size); } else if (SIZE_T_MAX / 3 * 2 <= size) { p = NULL; } else { size += (size + 1) / 2; p = realloc(buffer, size); } if (!p) { free(buffer); errno = ENOMEM; return -1; } buffer = p; buffer[size - 1] = 0; if (getfn(buffer, size - 1) == 0) { if (!buffer[size - 1]) break; } else if (errno != 0 && errno != ENAMETOOLONG && errno != EINVAL && errno != ENOMEM) { int rc = errno; free(buffer); errno = rc; return -1; } } *storage = buffer; return 0; } int stripdomain(char *hostname, char const *domainname) { size_t hlen, dlen; if (!domainname) return -1; hlen = strlen(hostname); dlen = strlen(domainname); if (hlen > dlen + 1 && hostname[hlen - dlen - 1] == '.' && strcasecmp(hostname + hlen - dlen, domainname) == 0) { hostname[hlen - dlen - 1] = 0; return 0; } return -1; } int get_host_domain_names(char **host_name_ptr, char **domain_name_ptr) { char *hostname; char *domainname = NULL; if (xgetname(gethostname, &hostname)) return -1; #if HAVE_GETDOMAINNAME if (use_getdomainname) { if (xgetname(getdomainname, &domainname)) { _pam_log(LOG_ERR, "getdomainname: %s", strerror(errno)); } else if (strcmp (domainname, "(none)") == 0) { free(domainname); domainname = NULL; } } #endif if (domainname) { stripdomain(hostname, domainname); } else if (use_resolve) { char *p = strchr(hostname, '.'); if (!p) { struct hostent *hp = gethostbyname(hostname); if (hp) { size_t len = strlen(hp->h_name); p = realloc(hostname, len + 1); if (!p) { free(hostname); errno = ENOMEM; return -1; } hostname = p; strcpy(hostname, hp->h_name); p = strchr(hostname, '.'); } } if (p) { *p++ = 0; domainname = strdup(p); if (!domainname) { int rc = errno; _pam_log(LOG_ERR, "getdomainname: %s", strerror(errno)); free(hostname); errno = rc; return -1; } } } *host_name_ptr = hostname; *domain_name_ptr = domainname; return 0; } static int check_netgroup0(pam_handle_t *pamh, int argc, const char **argv, const char *func) { int rc; char *host_name_buf = NULL; char *domain_name_buf = NULL; char const *user_name; debug_level = 0; host_name = NULL; domain_name = NULL; netgroup_name = NULL; use_getdomainname = 1; use_resolve = 1; sense = SENSE_ALLOW; gray_pam_init(PAM_AUTHINFO_UNAVAIL); gray_log_init(0, MODULE_NAME, LOG_AUTHPRIV); gray_parseopt(pam_opt, argc, argv); if (!netgroup_name) { _pam_log(LOG_ERR, "no netgroup name given"); return PAM_AUTHINFO_UNAVAIL; } /* * get username */ rc = pam_get_user(pamh, &user_name, "login: "); if (rc == PAM_SUCCESS) { DEBUG(10, ("username [%s] obtained", user_name)); } else { _pam_log(LOG_NOTICE, "can't get username"); return PAM_AUTHINFO_UNAVAIL; } if (!host_name || !domain_name) { if (get_host_domain_names (&host_name_buf, &domain_name_buf)) { _pam_log(LOG_ERR, "%s", strerror(errno)); return PAM_SERVICE_ERR; } if (!host_name) { if (!host_name_buf) { _pam_log(LOG_NOTICE, "can't get hostname"); return PAM_AUTHINFO_UNAVAIL; } host_name = host_name_buf; } if (!domain_name) { if (!domain_name_buf) { _pam_log(LOG_NOTICE, "can't get domainname"); return PAM_AUTHINFO_UNAVAIL; } domain_name = domain_name_buf; } } DEBUG(1,("checking (%s, %s, %s)", host_name, user_name, domain_name)); rc = innetgr(netgroup_name, host_name, user_name, domain_name); DEBUG(1,("netgroup %s, triple (%s, %s, %s): %d", netgroup_name, host_name, user_name, domain_name, rc)); free(host_name_buf); free(domain_name_buf); if (sense == SENSE_DENY) rc = !rc; return rc ? PAM_SUCCESS : PAM_AUTH_ERR; } static int check_netgroup(pam_handle_t *pamh, int argc, const char **argv, const char *func) { int rc; DEBUG(90,("enter %s", func)); rc = check_netgroup0(pamh, argc, argv, __FUNCTION__); DEBUG(90,("leave %s=%d", func, rc)); return rc; } PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) { return check_netgroup(pamh, argc, argv, __FUNCTION__); } PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh,int flags,int argc,const char **argv) { return check_netgroup(pamh, argc, argv, __FUNCTION__); } PAM_EXTERN int pam_sm_acct_mgmt (pam_handle_t *pamh, int flags, int argc, const char **argv) { return check_netgroup(pamh, argc, argv, __FUNCTION__); } PAM_EXTERN int pam_sm_open_session (pam_handle_t *pamh, int flags, int argc, const char **argv) { return check_netgroup(pamh, argc, argv, __FUNCTION__); } PAM_EXTERN int pam_sm_close_session (pam_handle_t *pamh, int flags, int argc, const char **argv) { return check_netgroup(pamh, argc, argv, __FUNCTION__); } #ifdef PAM_STATIC /* static module data */ struct pam_module _pam_log_modstruct = { MODULE_NAME, pam_sm_authenticate, pam_sm_setcred, pam_sm_acct_mgmt, pam_sm_open_session, pam_sm_close_session, pam_sm_chauthtok, }; #endif /* end of module definition */