diff options
Diffstat (limited to 'pam_innetgr/pam_innetgr.c')
-rw-r--r-- | pam_innetgr/pam_innetgr.c | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/pam_innetgr/pam_innetgr.c b/pam_innetgr/pam_innetgr.c new file mode 100644 index 0000000..f1f5e5d --- /dev/null +++ b/pam_innetgr/pam_innetgr.c @@ -0,0 +1,319 @@ +/* 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 <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE__PAM_ACONF_H +#include <security/_pam_aconf.h> +#endif +#include <stdlib.h> +#include <netdb.h> +#include "graypam.h" + +#ifndef LINUX_PAM +#include <security/pam_appl.h> +#endif +#include <security/pam_modules.h> + +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 */ + + |