aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2012-08-01 21:07:27 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2012-08-01 21:14:22 +0300
commitd211d9ec0cf708b047a0fb0d4019a16a806bbf4c (patch)
treeecffe1e8c9939e794ca7fbfc066e28c0e2aff2aa
parentf6cde78d9891ad9b07daf6f1b8a822a2a26a250c (diff)
downloadpam-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.texi8
-rw-r--r--pam_ldaphome/pam_ldaphome.c124
-rw-r--r--pamck/pamck.c2
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;

Return to:

Send suggestions and report system problems to the System administrator.