diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2012-08-01 21:07:27 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2012-08-01 21:14:22 +0300 |
commit | d211d9ec0cf708b047a0fb0d4019a16a806bbf4c (patch) | |
tree | ecffe1e8c9939e794ca7fbfc066e28c0e2aff2aa | |
parent | f6cde78d9891ad9b07daf6f1b8a822a2a26a250c (diff) | |
download | pam-modules-d211d9ec0cf708b047a0fb0d4019a16a806bbf4c.tar.gz pam-modules-d211d9ec0cf708b047a0fb0d4019a16a806bbf4c.tar.bz2 |
Improve pam_ldaphome
New keywords: bindpwfile and keyfile-mode.
* doc/pam-modules.texi: Document bindpwfile and keyfile-mode.
* pam_ldaphome/pam_ldaphome.c (ldap_bind): Read password from
file, if bindpwfile is given.
(store_pubkeys): Optionally enforce file mode, given by the
keyfile-mode configuration statement.
* pamck/pamck.c (main): Fix a typo.
-rw-r--r-- | doc/pam-modules.texi | 8 | ||||
-rw-r--r-- | pam_ldaphome/pam_ldaphome.c | 124 | ||||
-rw-r--r-- | pamck/pamck.c | 2 |
3 files changed, 116 insertions, 18 deletions
diff --git a/doc/pam-modules.texi b/doc/pam-modules.texi index d263d57..e7b460f 100644 --- a/doc/pam-modules.texi +++ b/doc/pam-modules.texi @@ -1210,12 +1210,16 @@ binddn cn=Manager,dc=gnu,dc=org,dc=ua @deffn {pam_ldaphome config} bindpw password If @code{binddn} statement is used, this statement supplies the password for simple authentication. @end deffn +@deffn {pam_ldaphome config} bindpwfile file +Read password for simple authentication from @var{file}. +@end deffn + @deffn {pam_ldaphome config} tls val Controls whether TLS is desired or required. If @var{val} is @samp{no} (the default), TLS will not be used. If it is @samp{yes}, the module will issue the @samp{StartTLS} command, but will continue anyway if it fails. Finally, if @var{val} is @samp{only}, TLS is mandatory, and the module will not establish LDAP connection unless @@ -1256,12 +1260,16 @@ directory to the newly created home. The default size is 16384 bytes. @end deffn @deffn {pam_ldaphome config} home-dir-mode mode Sets the mode (octal) for the created user directories. @end deffn +@deffn {pam_ldaphome config} keyfile-mode mode +Sets the mode (octal) for the created authorized keys file. +@end deffn + @deffn {pam_ldaphome config} authorized_keys name Sets the pathname (relative to the home directory) for the authorized keys file. The default is @samp{.ssh/authorized_keys}. For normal operation, this value must be the same as the value of @samp{AuthorizedKeysFile} variable in @file{sshd_config}. Unless you change the latter, there's no need to edit it. diff --git a/pam_ldaphome/pam_ldaphome.c b/pam_ldaphome/pam_ldaphome.c index 9e5b440..83c10c1 100644 --- a/pam_ldaphome/pam_ldaphome.c +++ b/pam_ldaphome/pam_ldaphome.c @@ -199,13 +199,13 @@ parse_ldap_uri(const char *uri) && (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) || + 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; @@ -285,20 +285,20 @@ parse_ldap_uri(const char *uri) lud->lud_next = NULL; ldap_free_urldesc(lud); } if (ludlist) { - ldap_free_urldesc (ludlist); + ldap_free_urldesc(ludlist); return NULL; } else if (!urls) return NULL; ldapuri = argcv_concat(nurls, urls); if (!ldapuri) _pam_log(LOG_ERR, "%s", strerror(errno)); - ber_memvfree ((void **)urls); + ber_memvfree((void **)urls); return ldapuri; } static void ldap_unbind(LDAP *ld); static LDAP * @@ -309,20 +309,20 @@ ldap_connect(struct gray_env *env) LDAP *ld = NULL; int protocol = LDAP_VERSION3; char *val; unsigned long lval; if (ldap_debug_level) { - if (ber_set_option (NULL, LBER_OPT_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, + 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); } @@ -408,49 +408,124 @@ ldap_connect(struct gray_env *env) /* FIXME: Timeouts, SASL, etc. */ return ld; } static int -ldap_bind (LDAP *ld, struct gray_env *env) +full_read(int fd, char *file, char *buf, size_t size) +{ + while (size) { + ssize_t n; + + n = read(fd, buf, size); + if (n == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + _pam_log(LOG_ERR, "error reading from %s: %s", + file, strerror(errno)); + return -1; + } else if (n == 0) { + _pam_log(LOG_ERR, "short read from %s", file); + return -1; + } + + buf += n; + size -= n; + } + return 0; +} + +static int +get_passwd(struct gray_env *env, struct berval *pwd, char **palloc) +{ + char *file; + + file = gray_env_get(env, "bindpwfile"); + if (file) { + struct stat st; + int fd, rc; + char *mem, *p; + + fd = open(file, O_RDONLY); + if (fd == -1) { + _pam_log(LOG_ERR, "can't open password file %s: %s", + file, strerror(errno)); + return -1; + } + if (fstat(fd, &st)) { + _pam_log(LOG_ERR, "can't stat password file %s: %s", + file, strerror(errno)); + close(fd); + return -1; + } + mem = malloc(st.st_size + 1); + if (!mem) { + _pam_log(LOG_ERR, "can't allocate memory (%lu bytes)", + (unsigned long) st.st_size+1); + close(fd); + return -1; + } + rc = full_read(fd, file, mem, st.st_size); + close(fd); + if (rc) + return rc; + mem[st.st_size] = 0; + p = strchr(mem, '\n'); + if (p) + *p = 0; + *palloc = mem; + pwd->bv_val = mem; + } else + pwd->bv_val = gray_env_get(env, "bindpw"); + pwd->bv_len = pwd->bv_val ? strlen(pwd->bv_val) : 0; + return 0; +} + +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; + struct berval passwd; char *binddn; + char *alloc_ptr = NULL; 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; + + if (get_passwd(env, &passwd, &alloc_ptr)) + return 1; 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)); + free(alloc_ptr); return 1; } if (ldap_result(ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1) { _pam_log(LOG_ERR, "ldap_result failed"); + free(alloc_ptr); 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)); + ldap_err2string(rc)); + free(alloc_ptr); return 1; } if (ctrls) ldap_controls_free(ctrls); @@ -481,12 +556,14 @@ ldap_bind (LDAP *ld, struct gray_env *env) ber_memfree(matched); if (info) ber_memfree(info); if (refs) ber_memvfree((void **)refs); + free(alloc_ptr); + return !(err == LDAP_SUCCESS); } static void ldap_unbind(LDAP *ld) { @@ -780,13 +857,13 @@ read_link_name(const char *name, char **pbuf, size_t *psize, size_t *plen) break; } } if (rc) { if (buf) { - free (buf); + free(buf); buf = NULL; } size = 0; } *pbuf = buf; *psize = size; @@ -1134,37 +1211,53 @@ populate_homedir(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env) } free(buffer); return rc; } static int -store_pubkeys(char **keys, struct passwd *pw) +store_pubkeys(char **keys, struct passwd *pw, struct gray_env *env) { FILE *fp; int c; char *file_name; size_t homelen, pathlen, len; int retval, i; int update = 0; + int oldmask; + unsigned long mode; 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); + + switch (get_intval(env, "keyfile-mode", 8, &mode)) { + case -1: + return PAM_SERVICE_ERR; + case 1: + oldmask = -1; + break; + case 0: + oldmask = umask(0666 ^ (mode & 0777)); + } fp = fopen(file_name, "r+"); if (!fp && create_interdir(file_name, pw) == 0) { fp = fopen(file_name, "w"); update = 1; } + + if (oldmask != -1) + umask(oldmask); + if (!fp) { _pam_log(LOG_EMERG, "cannot open file %s: %s", file_name, strerror(errno)); free(file_name); return PAM_SERVICE_ERR; } @@ -1241,13 +1334,13 @@ import_public_key(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env) gray_expand_string(pamh, filter_pat, slist); gray_slist_append_char(slist, 0); filter = gray_slist_finish(slist); keys = get_pubkeys(ld, base, filter, attr); gray_slist_free(&slist); - retval = store_pubkeys(keys, pw); + retval = store_pubkeys(keys, pw, env); argcvz_free(keys); } ldap_unbind(ld); return retval; } @@ -1287,16 +1380,13 @@ create_home_dir(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env) } return PAM_SUCCESS; } PAM_EXTERN int -pam_sm_authenticate(pam_handle_t *pamh, - int flags, - int argc, - const char **argv) +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); diff --git a/pamck/pamck.c b/pamck/pamck.c index 8c5336e..497300b 100644 --- a/pamck/pamck.c +++ b/pamck/pamck.c @@ -159,13 +159,13 @@ main (int argc, char **argv) case 'g': if (strcmp(optarg, "help") == 0) { groupprint(); exit(0); } group = find_group(optarg); - if (!service) + if (!group) error(1, "no such management group, try `%s -g help' for the list", program_name); break; |