pam-modules/pam_innetgr/pam_innetgr.c
2022-02-04 09:38:17 +02:00

324 lines
7.2 KiB
C

/* This file is part of pam-modules.
Copyright (C) 2018-2022 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_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_setcred(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 */