Revamp build system. Add pam_ldaphome module.

* configure.ac: Test for ldap.
(AC_OUTPUT): Create pam_ldaphome/Makefile.
* acinclude.m4 (PM_ENABLE): Declare PAM_COND_<item>
conditional in addition to BUILD_PAM_<item> substitution
variable.
* Makefile.am (SUBDIRS): Include most modules via
conditionally defined Makefile variables.

* lib/graypam.h (gray_env): New struct.
(gray_env_get,gray_env_free,gray_env_read)
(gray_boolean_true_p): New protos.
* lib/env.c: New file.
* lib/Makefile.am (libgraypam_la_SOURCES): Add env.c

* pam_fshadow/Makefile.am: Remove BUILD_PAM_FSHADOW
substitution.
* pam_regex/Makefile.am: Remove BUILD_PAM_REGEX
substitution.
* pam_sql/pam_sql.c (free_config, boolean_true_p)
(read_config): Remove. Use gray_env_* functions
instead. All uses updated.
* pam_regex/pam_regex.c: Fix typo.

* pam_ldaphome/Makefile.am: New file.


git-svn-id: file:///svnroot/pam-modules/trunk@118 56984be4-0537-0410-a56c-fcb268c96130
This commit is contained in:
Sergey Poznyakoff 2012-05-13 09:48:57 +00:00
parent 993eff1366
commit 1a80b647d6
13 changed files with 1155 additions and 181 deletions

View file

@ -1,3 +1,32 @@
2012-05-13 Sergey Poznyakoff <gray@gnu.org.ua>
Revamp build system. Add pam_ldaphome module.
* configure.ac: Test for ldap.
(AC_OUTPUT): Create pam_ldaphome/Makefile.
* acinclude.m4 (PM_ENABLE): Declare PAM_COND_<item>
conditional in addition to BUILD_PAM_<item> substitution
variable.
* Makefile.am (SUBDIRS): Include most modules via
conditionally defined Makefile variables.
* lib/graypam.h (gray_env): New struct.
(gray_env_get,gray_env_free,gray_env_read)
(gray_boolean_true_p): New protos.
* lib/env.c: New file.
* lib/Makefile.am (libgraypam_la_SOURCES): Add env.c
* pam_fshadow/Makefile.am: Remove BUILD_PAM_FSHADOW
substitution.
* pam_regex/Makefile.am: Remove BUILD_PAM_REGEX
substitution.
* pam_sql/pam_sql.c (free_config, boolean_true_p)
(read_config): Remove. Use gray_env_* functions
instead. All uses updated.
* pam_regex/pam_regex.c: Fix typo.
* pam_ldaphome/Makefile.am: New file.
2011-09-29 Sergey Poznyakoff <gray@gnu.org.ua>
Allow installers to link modules with alternative crypt(3)

View file

@ -14,4 +14,24 @@
# with this program. If not, see <http://www.gnu.org/licenses/>.
AUTOMAKE_OPTIONS = gnits 1.8
SUBDIRS = doc lib pam_fshadow pam_regex pam_log pam_sql pamck
if PAM_COND_FSHADOW
FSHADOW_DIR=pam_fshadow
endif
if PAM_COND_REGEX
REGEX_DIR=pam_regex
endif
if PAM_COND_LOG
LOG_DIR=pam_regex
endif
if PAM_COND_LDAPHOME
LDAPHOME_DIR=pam_ldaphome
endif
SUBDIRS = \
doc\
lib\
$(FSHADOW_DIR)\
$(REGEX_DIR)\
$(LOG_DIR)\
pam_sql\
$(LDAPHOME_DIR)\
pamck

View file

@ -1,4 +1,4 @@
# Copyright (C) 2001, 2006, 2008-2011 Sergey Poznyakoff
# Copyright (C) 2001, 2006, 2008-2012 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
@ -18,13 +18,17 @@ AC_DEFUN([PM_ENABLE],[
AC_HELP_STRING([--disable-$1], [Disable pam_$1]),
[if test $build_$1 = probe; then
build_$1=$enableval
fi])
fi],
[build_$1=probe])
m4_pushdef([upmodname],translit($1, [a-z.-], [A-Z__]))
if test $build_$1 != no; then
build_$1=yes
m4_pushdef([upmodname],[PAM_]translit($1, [a-z.-], [A-Z__]))
AC_SUBST([BUILD_]upmodname, ['$(]upmodname[)'])
AC_SUBST([BUILD_PAM_]upmodname, ['$(]upmodname[)'])
$2
fi])
fi
AM_CONDITIONAL([PAM_COND_]upmodname, [test "$[]build_$1" = "yes"])
m4_popdef([upmodname])
])
dnl PM_FLUSHLEFT -- remove all whitespace at the beginning of lines
dnl This is useful for c-code which may include cpp statements

View file

@ -30,12 +30,6 @@ AC_SUBST(VI_CURRENT,1)
AC_SUBST(VI_REVISION,0)
AC_SUBST(VI_AGE,0)
dnl Modules
build_fshadow=probe
build_regex=probe
build_log=probe
build_pgsql=probe
dnl Checks for programs.
AC_PROG_CC
AC_PROG_INSTALL
@ -153,6 +147,14 @@ AC_SUBST(MYSQLLIBS)
AC_SUBST(PGSQLLIBS)
AC_SUBST(SQL_MODULES)
# LDAP support
PM_ENABLE(ldaphome)
if test $build_ldaphome = probe; then
AC_CHECK_LIB(ldap, ldap_bind,
[build_ldaphome=yes]
[build_ldaphome=no])
fi
## *****************
## debugging support
## *****************
@ -196,7 +198,7 @@ delim="-------------------------------------------------------------------"
echo $delim | tr '-' '*'
echo "Modules to build:"
res=
for module in fshadow regex log pgsql mysql
for module in fshadow regex log pgsql mysql ldaphome
do
modname=pam_$module
eval enable=\${build_$module}
@ -216,7 +218,8 @@ build_fshadow=$build_fshadow
build_regex=$build_regex
build_log=$build_log
build_pgsql=$build_pgsql
build_mysql=$build_mysql])
build_mysql=$build_mysql
build_ldaphome=$build_ldaphome])
AC_OUTPUT(Makefile
doc/Makefile
@ -225,4 +228,5 @@ AC_OUTPUT(Makefile
pam_regex/Makefile
pam_log/Makefile
pam_sql/Makefile
pam_ldaphome/Makefile
pamck/Makefile)

View file

@ -16,6 +16,7 @@
lib_LTLIBRARIES = libgraypam.la
libgraypam_la_SOURCES = \
env.c\
log.c\
mem.c\
slist.c\

168
lib/env.c Normal file
View file

@ -0,0 +1,168 @@
/* This file is part of pam-modules.
Copyright (C) 2008, 2010-2012 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/>. */
#include <graypam.h>
char *
gray_env_get(struct gray_env *env, const char *name)
{
for (; env; env = env->next)
if (strcmp(env->name, name) == 0)
return env->value;
return NULL;
}
void
gray_env_free(struct gray_env *env)
{
while (env) {
struct gray_env *next = env->next;
free(env->name);
free(env);
env = next;
}
}
int
gray_env_read(const char *file_name, struct gray_env **penv)
{
FILE *fp;
char *p;
int rc = 0;
int line = 0;
char buf[128];
gray_slist_t slist = NULL;
struct gray_env *config_env = NULL;
fp = fopen(file_name, "r");
if (!fp) {
_pam_log(LOG_ERR, "cannot open configuration file `%s': %s",
file_name, strerror (errno));
return 1;
}
config_env = NULL;
while (p = fgets(buf, sizeof buf, fp)) {
int len;
struct gray_env *env;
line++;
while (*p && isspace(*p))
p++;
len = strlen(p);
if (len == 0)
continue;
if (p[len-1] != '\n') {
if (!slist)
slist = gray_slist_create();
gray_slist_append(slist, p, len);
while (p = fgets(buf, sizeof buf, fp)) {
len = strlen(p);
gray_slist_append(slist, p, len);
if (p[len - 1] == '\n')
break;
}
gray_slist_append_char(slist, 0);
p = gray_slist_finish(slist);
len = strlen(p);
}
p[len-1] = 0;
len = gray_trim_ws(p);
if (*p == 0 || *p == '#')
continue;
if (p[len-1] == '\\') {
int err = 0;
/* Collect continuation lines */
if (!slist)
slist = gray_slist_create();
do {
gray_slist_append(slist, p, len - 1);
p = fgets (buf, sizeof buf, fp);
if (!p)
break;
line++;
len = strlen(p);
if (len == 0)
break;
if (p[len-1] != '\n') {
_pam_log(LOG_EMERG,
"%s:%d: string too long",
file_name, line);
err = 1;
break;
}
p[len-1] = 0;
len = gray_trim_ws(p);
} while (p[len-1] == '\\');
if (len)
gray_slist_append(slist, p, len);
gray_slist_append_char(slist, 0);
p = gray_slist_finish(slist);
if (err)
continue;
}
env = malloc(sizeof *env);
if (!env) {
_pam_log(LOG_EMERG, "not enough memory");
rc = 1;
break;
}
env->name = strdup(p);
if (!env->name) {
_pam_log(LOG_EMERG, "not enough memory");
free(env);
rc = 1;
break;
}
for (p = env->name; *p && !isspace(*p); p++)
;
if (*p)
*p++ = 0;
for (; *p && isspace(*p); p++)
;
if (!*p) {
_pam_log(LOG_EMERG, "%s:%d: not enough fields",
file_name, line);
free(env->name);
free(env);
continue;
}
env->value = p;
env->next = config_env;
config_env = env;
}
gray_slist_free(&slist);
fclose(fp);
*penv = config_env;
return rc;
}
int
gray_boolean_true_p(const char *value)
{
return strcmp(value, "yes") == 0
|| strcmp(value, "true") == 0
|| strcmp(value, "t") == 0;
}

View file

@ -196,5 +196,19 @@ struct keyword *gray_find_keyword(struct keyword *kwtab, const char *str,
int gray_trim_ws(char *str);
/* configuration file support */
struct gray_env {
struct gray_env *next;
char *name;
char *value;
};
char *gray_env_get(struct gray_env *env, const char *name);
void gray_env_free(struct gray_env *env);
int gray_env_read(const char *file_name, struct gray_env **penv);
int gray_boolean_true_p(const char *value);
#endif

View file

@ -14,9 +14,7 @@
# with this program. If not, see <http://www.gnu.org/licenses/>.
pamdir=@PAMDIR@
PAM_FSHADOW = pam_fshadow.la
pam_LTLIBRARIES = @BUILD_PAM_FSHADOW@
EXTRA_LTLIBRARIES = pam_fshadow.la
pam_LTLIBRARIES = pam_fshadow.la
pam_fshadow_la_SOURCES = pam_fshadow.c
AM_CPPFLAGS=-DMODULE_NAME=\"pam_fshadow\" -DSYSCONFDIR=\"${sysconfdir}\"

23
pam_ldaphome/Makefile.am Normal file
View file

@ -0,0 +1,23 @@
# This file is part of pam-modules.
# Copyright (C) 2012 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/>. */
include $(top_srcdir)/Make.rules
pamdir=@PAMDIR@
pam_LTLIBRARIES = pam_ldaphome.la
AM_CPPFLAGS=-DMODULE_NAME=\"pam_ldaphome\" -DSYSCONFDIR=\"${sysconfdir}\"
pam_ldaphome_la_SOURCES = pam_ldaphome.c
pam_ldaphome_la_LIBADD = @PAM_MISC@ -lldap

865
pam_ldaphome/pam_ldaphome.c Normal file
View file

@ -0,0 +1,865 @@
/* This file is part of pam-modules.
Copyright (C) 2005-2008, 2010-2011 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
#ifndef LINUX_PAM
# include <security/pam_appl.h>
#endif /* LINUX_PAM */
#include <security/pam_modules.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <ldap.h>
#include <pwd.h>
#include <grp.h>
#include "graypam.h"
/* indicate the following groups are defined */
#define PAM_SM_AUTH
static long debug_level;
static int cntl_flags;
static char *config_file_name;
static int ldap_debug_level;
/* FIXME: This should be read from sshd_config */
static char *authorized_keys_file=".ssh/authorized_keys";
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_bitmask, &cntl_flags, { CNTL_AUDIT } },
{ PAM_OPTSTR(waitdebug), pam_opt_null, NULL, { 0 },
gray_wait_debug_fun },
{ PAM_OPTSTR(config), pam_opt_string, &config_file_name },
{ NULL }
};
static void
_pam_parse(pam_handle_t *pamh, int argc, const char **argv)
{
cntl_flags = 0;
debug_level = 0;
config_file_name = SYSCONFDIR "/" MODULE_NAME ".conf";
gray_log_init(0, MODULE_NAME, LOG_AUTHPRIV);
gray_parseopt(pam_opt, argc, argv);
}
static void
argcv_free(int wc, char **wv)
{
int i;
for (i = 0; i < wc; i++) {
free(wv[i]);
free(wv);
}
}
static int
argcv_split(const char *str, int *pargc, char ***pargv)
{
int argc, i;
char **argv;
const char *p;
int rc = 0;
argc = 1;
for (p = str; *p; p++) {
if (*p == ' ')
argc++;
}
argv = calloc(argc + 1, sizeof(argv[0]));
if (!argv)
return 1;
for (i = 0, p = str;;) {
size_t len = strcspn(p, " ");
char *q = malloc(len + 1);
if (!q) {
rc = errno;
break;
}
memcpy(q, p, len);
q[len] = 0;
argv[i++] = q;
p += len;
if (p)
p += strspn(p, " ");
if (!*p)
break;
}
if (rc) {
argcv_free(argc, argv);
errno = rc;
return 1;
}
argv[i] = NULL;
*pargc = argc;
*pargv = argv;
return 0;
}
static char *
argcv_concat(int wc, char **wv)
{
char *res, *p;
size_t size = 0;
int i;
for (i = 0; i < wc; i++)
size += strlen(wv[i]) + 1;
size++;
res = malloc(size);
if (!res)
return 0;
for (p = res, i = 0; ; i++) {
strcpy(p, wv[i]);
p += strlen(wv[i]);
if (i < wc)
*p++ = ' ';
else
break;
}
*p = 0;
return res;
}
char *
parse_ldap_uri(const char *uri)
{
int wc;
char **wv;
LDAPURLDesc *ludlist, **ludp;
char **urls = NULL;
int nurls = 0;
char *ldapuri = NULL;
int rc;
rc = ldap_url_parse(uri, &ludlist);
if (rc != LDAP_URL_SUCCESS) {
_pam_log(LOG_ERR, "cannot parse LDAP URL(s)=%s (%d)",
uri, rc);
return NULL;
}
for (ludp = &ludlist; *ludp; ) {
LDAPURLDesc *lud = *ludp;
char **tmp;
if (lud->lud_dn && lud->lud_dn[0]
&& (lud->lud_host == NULL || lud->lud_host[0] == '\0')) {
/* if no host but a DN is provided, try
DNS SRV to gather the host list */
char *domain = NULL, *hostlist = NULL;
size_t i;
if (ldap_dn2domain (lud->lud_dn, &domain) ||
!domain) {
_pam_log(LOG_ERR,
"DNS SRV: cannot convert "
"DN=\"%s\" into a domain",
lud->lud_dn);
goto dnssrv_free;
}
rc = ldap_domain2hostlist(domain, &hostlist);
if (rc) {
_pam_log(LOG_ERR,
"DNS SRV: cannot convert "
"domain=%s into a hostlist",
domain);
goto dnssrv_free;
}
if (argcv_split(hostlist, &wc, &wv)) {
_pam_log(LOG_ERR,
"DNS SRV: could not parse "
"hostlist=\"%s\": %s",
hostlist, strerror(errno));
goto dnssrv_free;
}
tmp = realloc(urls, sizeof(char *) * (nurls + wc + 1));
if (!tmp) {
_pam_log(LOG_ERR,
"DNS SRV %s", strerror(errno));
goto dnssrv_free;
}
urls = tmp;
urls[nurls] = NULL;
for (i = 0; i < wc; i++) {
char *p = malloc(strlen(lud->lud_scheme) +
strlen(wv[i]) +
3);
if (!p) {
_pam_log(LOG_ERR, "DNS SRV %s",
strerror(errno));
goto dnssrv_free;
}
strcpy(p, lud->lud_scheme);
strcat(p, "//");
strcat(p, wv[i]);
urls[nurls + i + 1] = NULL;
urls[nurls + i] = p;
}
nurls += i;
dnssrv_free:
argcv_free(wc, wv);
ber_memfree(hostlist);
ber_memfree(domain);
} else {
tmp = realloc(urls, sizeof(char *) * (nurls + 2));
if (!tmp) {
_pam_log(LOG_ERR,
"DNS SRV %s", strerror(errno));
break;
}
urls = tmp;
urls[nurls + 1] = NULL;
urls[nurls] = ldap_url_desc2str(lud);
if (!urls[nurls]) {
_pam_log(LOG_ERR, "DNS SRV %s",
strerror(errno));
break;
}
nurls++;
}
*ludp = lud->lud_next;
lud->lud_next = NULL;
ldap_free_urldesc(lud);
}
if (ludlist) {
ldap_free_urldesc (ludlist);
return NULL;
} else if (!urls)
return NULL;
ldapuri = argcv_concat(wc, wv);
if (!ldapuri)
_pam_log(LOG_ERR, "%s", strerror(errno));
ber_memvfree ((void **)urls);
return ldapuri;
}
static LDAP *
ldap_connect(struct gray_env *env)
{
int rc;
char *ldapuri = NULL;
LDAP *ld = NULL;
int protocol = LDAP_VERSION3; /* FIXME: must be configurable */
char *val;
if (ldap_debug_level) {
if (ber_set_option (NULL, LBER_OPT_DEBUG_LEVEL,
&ldap_debug_level)
!= LBER_OPT_SUCCESS )
_pam_log(LOG_ERR,
"cannot set LBER_OPT_DEBUG_LEVEL %d",
ldap_debug_level);
if (ldap_set_option (NULL, LDAP_OPT_DEBUG_LEVEL,
&ldap_debug_level)
!= LDAP_OPT_SUCCESS )
_pam_log(LOG_ERR,
"could not set LDAP_OPT_DEBUG_LEVEL %d",
ldap_debug_level);
}
val = gray_env_get(env, "uri");
if (val) {
ldapuri = parse_ldap_uri(val);
if (!ldapuri)
return NULL;
}
DEBUG(2, ("constructed LDAP URI: %s",
ldapuri ? ldapuri : "<DEFAULT>"));
rc = ldap_initialize(&ld, ldapuri);
if (rc != LDAP_SUCCESS) {
_pam_log(LOG_ERR,
"cannot create LDAP session handle for "
"URI=%s (%d): %s",
ldapuri, rc, ldap_err2string(rc));
free(ldapuri);
return NULL;
}
free(ldapuri);
val = gray_env_get(env, "tls");
if (val && gray_boolean_true_p(val)) {
rc = ldap_start_tls_s(ld, NULL, NULL);
if (rc != LDAP_SUCCESS) {
_pam_log(LOG_ERR,
"ldap_start_tls failed: %s",
ldap_err2string(rc));
/* try to continue anyway, to avoid memory
leek (ld not being freed) */
}
}
ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &protocol);
/* FIXME: Timeouts, SASL, etc. */
return ld;
}
static int
ldap_bind (LDAP *ld, struct gray_env *env)
{
int msgid, err, rc;
LDAPMessage *result;
LDAPControl **ctrls;
char msgbuf[256];
char *matched = NULL;
char *info = NULL;
char **refs = NULL;
static struct berval passwd;
char *binddn;
binddn = gray_env_get(env, "binddn");
passwd.bv_val = gray_env_get(env, "bindpw");
passwd.bv_len = passwd.bv_val ? strlen(passwd.bv_val) : 0;
msgbuf[0] = 0;
rc = ldap_sasl_bind(ld, binddn, LDAP_SASL_SIMPLE, &passwd,
NULL, NULL, &msgid);
if (msgid == -1) {
_pam_log(LOG_ERR,
"ldap_sasl_bind(SIMPLE) failed: %s",
ldap_err2string(rc));
return 1;
}
if (ldap_result(ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1) {
_pam_log(LOG_ERR, "ldap_result failed");
return 1;
}
rc = ldap_parse_result(ld, result, &err, &matched, &info, &refs,
&ctrls, 1);
if (rc != LDAP_SUCCESS) {
_pam_log(LOG_ERR, "ldap_parse_result failed: %s",
ldap_err2string (rc));
return 1;
}
if (ctrls)
ldap_controls_free(ctrls);
if (err != LDAP_SUCCESS
|| msgbuf[0]
|| (matched && matched[0])
|| (info && info[0])
|| refs) {
/* FIXME: Use debug output for that */
DEBUG(2,("ldap_bind: %s (%d)%s",
ldap_err2string(err), err, msgbuf));
if (matched && *matched)
DEBUG(2,("matched DN: %s", matched));
if (info && *info)
DEBUG(2,("additional info: %s", info));
if (refs && *refs) {
int i;
DEBUG(3,("referrals:"));
for (i = 0; refs[i]; i++)
DEBUG(3,("%s", refs[i]));
}
}
if (matched)
ber_memfree(matched);
if (info)
ber_memfree(info);
if (refs)
ber_memvfree((void **)refs);
return !(err == LDAP_SUCCESS);
}
static void
ldap_unbind(LDAP *ld)
{
if (ld) {
ldap_set_option(ld, LDAP_OPT_SERVER_CONTROLS, NULL);
ldap_unbind_ext(ld, NULL, NULL);
}
}
static char *
get_ldap_attr(LDAP *ld, LDAPMessage *msg, const char *attr)
{
int rc;
BerElement *ber = NULL;
struct berval bv;
char *ufn = NULL;
char *val;
struct berval **values;
rc = ldap_get_dn_ber(ld, msg, &ber, &bv);
ufn = ldap_dn2ufn(bv.bv_val);
DEBUG(2, ("INFO: %s", ufn));
ldap_memfree(ufn);
values = ldap_get_values_len(ld, msg, attr);
if (!values || !values[0]) {
_pam_log(LOG_ERR,
"LDAP attribute `%s' has NULL value",
attr);
return NULL;
}
val = strdup(values[0]->bv_val);
if (!val)
_pam_log(LOG_ERR, "%s", strerror(errno));
else
DEBUG(1, ("pubkey: %s", val));
ldap_value_free_len(values);
return val;
}
static char *
ldap_search(LDAP *ld, const char *base, const char *filter, const char *attr)
{
int rc;
LDAPMessage *res, *msg;
ber_int_t msgid;
char *attrs[2];
char *ret;
attrs[0] = (char*) attr;
attrs[1] = NULL;
rc = ldap_search_ext(ld, base, LDAP_SCOPE_SUBTREE,
filter, attrs, 0,
NULL, NULL, NULL, -1, &msgid);
if (rc != LDAP_SUCCESS) {
_pam_log(LOG_ERR, "ldap_search_ext: %s", ldap_err2string(rc));
return NULL;
}
rc = ldap_result(ld, msgid, LDAP_MSG_ALL, NULL, &res);
if (rc < 0) {
_pam_log(LOG_ERR, "ldap_result failed");
return NULL;
}
rc = ldap_count_entries(ld, res);
if (rc == 0) {
_pam_log(LOG_ERR, "not enough entires");
return NULL;
}
if (rc > 1)
_pam_log(LOG_NOTICE, "LDAP: too many entries for filter %s",
filter);
msg = ldap_first_entry(ld, res);
ret = get_ldap_attr(ld, msg, attr);
ldap_msgfree(res);
return ret;
}
static int
get_intval(struct gray_env *env, const char *name, unsigned long *pv)
{
char *p;
char *v = gray_env_get(env, name);
if (!v)
return 1;
*pv = strtoul(v, &p, 10);
if (*p) {
_pam_log(LOG_ERR, "configuration variable %s is not integer",
name);
return 1;
}
return 0;
}
static int
check_groups(int gc, char **gv, const char *username)
{
int i;
for (i = 0; i < gc; i++) {
struct group *gp = getgrnam(gv[i]);
if (gp) {
char **p;
for (p = gp->gr_mem; *p; p++)
if (strcmp(username, *p) == 0)
return 0;
}
}
return 1;
}
static int
check_user_groups(pam_handle_t *pamh, struct gray_env *env,
struct passwd **ppw, int *retval)
{
int rc;
const char *username;
struct passwd *pw;
unsigned long ival;
char *sval;
rc = pam_get_user(pamh, &username, NULL);
if (rc != PAM_SUCCESS || !username) {
DEBUG(1,("can not get the username"));
*retval = rc;
return 1;
}
pw = getpwnam(username);
if (!pw) {
*retval = PAM_USER_UNKNOWN;
return 1;
}
*ppw = pw;
if (get_intval(env, "min-uid", &ival) == 0) {
if (pw->pw_uid < ival) {
DEBUG(10, ("ignoring user %s: has UID < %lu",
username, ival));
*retval = PAM_SUCCESS;
return 1;
}
}
if (get_intval(env, "min-gid", &ival) == 0) {
if (pw->pw_gid < ival) {
DEBUG(10, ("ignoring user %s: has GID < %lu",
username, ival));
*retval = PAM_SUCCESS;
return 1;
}
}
sval = gray_env_get(env, "allow-groups");
if (sval) {
int gc;
char **gv;
int rc;
if (argcv_split(sval, &gc, &gv)) {
_pam_log(LOG_ERR, "cannot split allow-groups: %s",
strerror(errno));
*retval = PAM_AUTH_ERR;
return 1;
}
rc = check_groups(gc, gv, username);
argcv_free(gc, gv);
if (rc) {
DEBUG(10, ("ignoring user %s: not in allowed group list",
username, ival));
*retval = PAM_SUCCESS;
return 1;
}
}
return 0;
}
static int
populate_homedir(struct passwd *pw, const char *skel)
{
// FIXME!!!
_pam_log(LOG_ERR, "populate_homedir is not yet implemented!");
return 1;
}
/* Create the directory DIR, eventually creating all intermediate directories
starting from DIR + BASELEN. */
int
create_hierarchy(char *dir, size_t baselen)
{
int rc;
struct stat st;
char *p;
if (stat(dir, &st) == 0) {
if (!S_ISDIR(st.st_mode)) {
_pam_log(LOG_ERR, "component %s is not a directory",
dir);
return 1;
}
return 0;
} else if (errno != ENOENT) {
_pam_log(LOG_ERR, "cannot stat file %s: %s",
dir, strerror(errno));
return 1;
}
p = strrchr(dir, '/');
if (p) {
if (p - dir + 1 < baselen) {
_pam_log(LOG_ERR, "base directory %s does not exist",
dir);
return 1;
}
*p = 0;
}
rc = create_hierarchy(dir, baselen);
if (rc == 0) {
if (p)
*p = '/';
if (mkdir(dir, 0755)) {
_pam_log(LOG_ERR, "cannot create directory %s: %s",
dir, strerror(errno));
rc = 1;
}
}
return rc;
}
int
create_interdir(const char *path, struct passwd *pw)
{
char *dir, *p;
size_t len;
int rc;
p = strrchr(path, '/');
if (!p)
return 1;
len = p - path;
dir = gray_malloc(len + 1);
memcpy(dir, path, len);
dir[len] = 0;
rc = create_hierarchy(dir, strlen(pw->pw_dir));
if (rc == 0)
chown(dir, pw->pw_uid, pw->pw_gid);
free(dir);
return rc;
}
static void
store_pubkey(const char *key, struct passwd *pw)
{
FILE *fp;
const char *kp;
int c;
int found = 0;
char *file_name;
size_t homelen, pathlen, len;
homelen = strlen(pw->pw_dir);
pathlen = strlen(authorized_keys_file);
len = homelen + pathlen;
if (pw->pw_dir[homelen - 1] != '/')
len++;
file_name = gray_malloc(len + 1);
memcpy(file_name, pw->pw_dir, homelen);
if (pw->pw_dir[homelen - 1] != '/')
file_name[homelen++] = '/';
strcpy(file_name + homelen, authorized_keys_file);
fp = fopen(file_name, "a+");
if (!fp && create_interdir(file_name, pw) == 0)
fp = fopen(file_name, "a+");
if (!fp) {
_pam_log(LOG_EMERG, "cannot open file %s: %s",
file_name, strerror(errno));
free(file_name);
return;
}
free(file_name);
fchown(fileno(fp), pw->pw_uid, pw->pw_gid);
kp = key;
while (!feof(fp)) {
while (*kp && (c = getc(fp)) != EOF && c == *kp)
kp++;
if (*kp == 0) {
DEBUG(2, ("key found"));
found = 1;
break;
}
kp = key;
if (c != '\n') {
if (c != EOF) {
while ((c = getc(fp)) != EOF && c != '\n')
;
}
if (c == EOF) {
if (ftell(fp))
fputc('\n', fp);
break;
}
}
}
if (!found) {
fwrite(key, strlen(key), 1, fp);
fputc('\n', fp);
}
fclose(fp);
}
static int
import_public_key(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env)
{
LDAP *ld;
int retval;
const char *base = gray_env_get(env, "base");
const char *filter_pat = gray_env_get(env, "filter");
const char *attr = gray_env_get(env, "pubkey-attr");
if (!filter_pat) {
_pam_log(LOG_ERR, "configuration variable `filter' not set");
return PAM_SERVICE_ERR;
}
if (!attr) {
_pam_log(LOG_ERR, "configuration variable `attr' not set");
return PAM_SERVICE_ERR;
}
ld = ldap_connect(env);
if (!ld)
return PAM_SERVICE_ERR;
if (ldap_bind(ld, env))
retval = PAM_SERVICE_ERR;
else {
char *filter;
gray_slist_t slist;
char *pubkey;
slist = gray_slist_create();
gray_expand_string(pamh, filter_pat, slist);
gray_slist_append_char(slist, 0);
filter = gray_slist_finish(slist);
pubkey = ldap_search(ld, base, filter, attr);
gray_slist_free(&slist);
store_pubkey(pubkey, pw);
free(pubkey);
retval = PAM_SUCCESS;
}
ldap_unbind(ld);
return retval;
}
static int
create_home_dir(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env)
{
struct stat st;
char *s;
if (stat(pw->pw_dir, &st)) {
if (errno != ENOENT) {
_pam_log(LOG_ERR, "cannot stat home directory %s: %s",
pw->pw_dir, strerror(errno));
return 1;
}
/* FIXME: mode must be configurable */
if (mkdir(pw->pw_dir, 0775)) {
_pam_log(LOG_ERR, "cannot create %s: %s",
pw->pw_dir, strerror(errno));
return 1;
}
chown(pw->pw_dir, pw->pw_uid, pw->pw_gid);
} else if (!S_ISDIR(st.st_mode)) {
_pam_log(LOG_ERR, "%s exists, but is not a directory",
pw->pw_dir);
return 1;
}
s = gray_env_get(env, "skel");
if (s) {
if (stat(s, &st)) {
_pam_log(LOG_ERR, "cannot stat skeleton directory %s: %s",
pw->pw_dir, strerror(errno));
return 1;
} else if (!S_ISDIR(st.st_mode)) {
_pam_log(LOG_ERR, "%s exists, but is not a directory",
pw->pw_dir);
return 1;
}
populate_homedir(pw, s);
}
return 0;
}
PAM_EXTERN int
pam_sm_authenticate(pam_handle_t *pamh,
int flags,
int argc,
const char **argv)
{
int retval = PAM_AUTH_ERR;
struct gray_env *env;
_pam_parse(pamh, argc, argv);
DEBUG(90,("enter pam_sm_authenticate"));
gray_pam_init(PAM_AUTHINFO_UNAVAIL);
if (gray_env_read(config_file_name, &env) == 0) {
struct passwd *pw;
if (check_user_groups(pamh, env, &pw, &retval) == 0) {
if (create_home_dir(pamh, pw, env) == 0 &&
import_public_key(pamh, pw, env) == 0)
retval = PAM_TRY_AGAIN;
}
gray_env_free(env);
}
DEBUG(90,("exit pam_sm_authenticate: %d", retval));
return retval;
}
PAM_EXTERN int
pam_sm_setcred(pam_handle_t *pamh,
int flags,
int argc,
const char **argv)
{
return PAM_SUCCESS;
}
#ifdef PAM_STATIC
struct pam_module _pam_ldaphome_modstruct = {
"pam_ldaphome", /* name of the module */
pam_sm_authenticate,
pam_sm_setcred,
NULL,
NULL,
NULL,
NULL
};
#endif

View file

@ -14,9 +14,8 @@
# with this program. If not, see <http://www.gnu.org/licenses/>.
pamdir=@PAMDIR@
PAM_REGEX = pam_regex.la
pam_LTLIBRARIES = @BUILD_PAM_REGEX@
EXTRA_LTLIBRARIES = pam_regex.la
PAM_REGEX =
pam_LTLIBRARIES = pam_regex.la
pam_regex_la_SOURCES = pam_regex.c
AM_CPPFLAGS=-DMODULE_NAME=\"pam_regex\" -DSYSCONFDIR=\"${sysconfdir}\"

View file

@ -194,7 +194,7 @@ pam_sm_setcred(pam_handle_t *pamh,
#ifdef PAM_STATIC
struct pam_module _pam_radius_modstruct = {
struct pam_module _pam_regex_modstruct = {
"pam_regex", /* name of the module */
pam_sm_authenticate,
pam_sm_setcred,

View file

@ -141,44 +141,12 @@ _pam_get_password(pam_handle_t *pamh, char **password, const char *prompt)
/* Configuration */
typedef struct config_env env_t;
struct config_env {
env_t *next;
char *name;
char *value;
};
static env_t *config_env;
static struct gray_env *config_env;
char *
gpam_sql_find_config(const char *name)
{
env_t *env;
for (env = config_env; env; env = env->next)
if (strcmp(env->name, name) == 0)
return env->value;
return NULL;
}
static void
free_config()
{
env_t *env = config_env;
while (env) {
env_t *next = env->next;
free(env->name);
free(env);
env = next;
}
config_env = NULL;
}
static int
boolean_true_p(const char *value)
{
return strcmp(value, "yes") == 0
|| strcmp(value, "true") == 0
|| strcmp(value, "t") == 0;
return gray_env_get(config_env, name);
}
int
@ -186,130 +154,10 @@ gpam_sql_check_boolean_config(const char *name, int defval)
{
const char *value = gpam_sql_find_config(name);
if (value)
defval = boolean_true_p(value);
defval = gray_boolean_true_p(value);
return defval;
}
static int
read_config ()
{
FILE *fp;
char *p;
int rc = 0;
int line = 0;
char buf[128];
gray_slist_t slist = NULL;
fp = fopen (gpam_sql_config_file, "r");
if (!fp) {
_pam_log(LOG_ERR, "cannot open configuration file `%s': %s",
gpam_sql_config_file, strerror (errno));
return 1;
}
config_env = NULL;
while (p = fgets (buf, sizeof buf, fp)) {
int len;
env_t *env;
line++;
while (*p && isspace(*p))
p++;
len = strlen(p);
if (len == 0)
continue;
if (p[len-1] != '\n') {
if (!slist)
slist = gray_slist_create();
gray_slist_append(slist, p, len);
while (p = fgets(buf, sizeof buf, fp)) {
len = strlen(p);
gray_slist_append(slist, p, len);
if (p[len - 1] == '\n')
break;
}
gray_slist_append_char(slist, 0);
p = gray_slist_finish(slist);
len = strlen(p);
}
p[len-1] = 0;
len = gray_trim_ws(p);
if (*p == 0 || *p == '#')
continue;
if (p[len-1] == '\\') {
int err = 0;
/* Collect continuation lines */
if (!slist)
slist = gray_slist_create();
do {
gray_slist_append(slist, p, len - 1);
p = fgets (buf, sizeof buf, fp);
if (!p)
break;
line++;
len = strlen(p);
if (len == 0)
break;
if (p[len-1] != '\n') {
_pam_log(LOG_EMERG,
"%s:%d: string too long",
gpam_sql_config_file, line);
err = 1;
break;
}
p[len-1] = 0;
len = gray_trim_ws(p);
} while (p[len-1] == '\\');
if (len)
gray_slist_append(slist, p, len);
gray_slist_append_char(slist, 0);
p = gray_slist_finish(slist);
if (err)
continue;
}
env = malloc(sizeof *env);
if (!env) {
_pam_log(LOG_EMERG, "not enough memory");
rc = 1;
break;
}
env->name = strdup(p);
if (!env->name) {
_pam_log(LOG_EMERG, "not enough memory");
free(env);
rc = 1;
break;
}
for (p = env->name; *p && !isspace(*p); p++)
;
if (*p)
*p++ = 0;
for (; *p && isspace(*p); p++)
;
if (!*p) {
_pam_log(LOG_EMERG, "%s:%d: not enough fields",
gpam_sql_config_file, line);
free(env->name);
free(env);
continue;
}
env->value = p;
env->next = config_env;
config_env = env;
}
gray_slist_free(&slist);
fclose(fp);
return rc;
}
const char *
gpam_sql_get_query(pam_handle_t *pamh, const char *name, gray_slist_t *pslist,
@ -364,7 +212,6 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
const char *username;
char *password;
int retval = PAM_AUTH_ERR;
gray_pam_init(PAM_SERVICE_ERR);
/* parse arguments */
@ -386,7 +233,7 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
return PAM_SERVICE_ERR;
}
if (read_config())
if (gray_env_read(gpam_sql_config_file, &config_env))
retval = PAM_SERVICE_ERR;
else {
gray_slist_t slist;
@ -399,7 +246,8 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
gray_slist_free(&slist);
}
free_config();
gray_env_free(config_env);
config_env = NULL;
switch (retval) {
case PAM_ACCT_EXPIRED:
@ -433,7 +281,7 @@ sql_session_mgmt(pam_handle_t *pamh, int flags,
/* parse arguments */
_pam_parse(argc, argv);
if (read_config())
if (gray_env_read(gpam_sql_config_file, &config_env))
retval = PAM_SERVICE_ERR;
else {
gray_slist_t slist;
@ -443,7 +291,8 @@ sql_session_mgmt(pam_handle_t *pamh, int flags,
gray_slist_free(&slist);
}
free_config();
gray_env_free(config_env);
config_env = NULL;
return retval;
}