Allow for multiple authorized keys.

* pam_ldaphome/pam_ldaphome.c (argcvz_free,trimnl): New functions.
(get_ldap_attr): Trim trailing newlines from the return value.
(ldap_search): Rename to get_pubkeys. Return null-terminated array
of sorted lexicographically keys.
(store_pubkey): Rename to store_pubkeys, take char ** as its
first argument. Store all keys unless the file already contains
exactly that set of keys.
(import_public_key): Call store_pubkeys. Use argcvz_free to
free the keys.
This commit is contained in:
Sergey Poznyakoff 2012-05-15 18:22:08 +03:00
parent 381fbf3365
commit 8a9a1ee295

View file

@ -70,9 +70,18 @@ argcv_free(int wc, char **wv)
{ {
int i; int i;
for (i = 0; i < wc; i++) { for (i = 0; i < wc; i++)
free(wv[i]); free(wv[i]);
free(wv);
} }
static void
argcvz_free(char **wv)
{
int i;
for (i = 0; wv[i]; i++)
free(wv[i]);
free(wv); free(wv);
} }
@ -422,6 +431,15 @@ ldap_unbind(LDAP *ld)
} }
} }
static void
trimnl(char *s)
{
size_t len = strlen(s);
while (len > 0 && s[len-1] == '\n')
--len;
s[len] = 0;
}
static char * static char *
get_ldap_attr(LDAP *ld, LDAPMessage *msg, const char *attr) get_ldap_attr(LDAP *ld, LDAPMessage *msg, const char *attr)
{ {
@ -447,20 +465,28 @@ get_ldap_attr(LDAP *ld, LDAPMessage *msg, const char *attr)
val = strdup(values[0]->bv_val); val = strdup(values[0]->bv_val);
if (!val) if (!val)
_pam_log(LOG_ERR, "%s", strerror(errno)); _pam_log(LOG_ERR, "%s", strerror(errno));
else else {
trimnl(val);
DEBUG(1, ("pubkey: %s", val)); DEBUG(1, ("pubkey: %s", val));
}
ldap_value_free_len(values); ldap_value_free_len(values);
return val; return val;
} }
static char * static int
ldap_search(LDAP *ld, const char *base, const char *filter, const char *attr) keycmp(const void *a, const void *b)
{
return strcmp(a, b);
}
static char **
get_pubkeys(LDAP *ld, const char *base, const char *filter, const char *attr)
{ {
int rc; int rc;
LDAPMessage *res, *msg; LDAPMessage *res, *msg;
ber_int_t msgid; ber_int_t msgid;
char *attrs[2]; char *attrs[2];
char *ret; char **ret;
attrs[0] = (char*) attr; attrs[0] = (char*) attr;
attrs[1] = NULL; attrs[1] = NULL;
@ -480,16 +506,29 @@ ldap_search(LDAP *ld, const char *base, const char *filter, const char *attr)
} }
rc = ldap_count_entries(ld, res); 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 = calloc(rc + 1, sizeof(ret[0]));
ret = get_ldap_attr(ld, msg, attr); if (!ret)
_pam_log(LOG_ERR, "%s", strerror(errno));
else {
int i = 0;
for (msg = ldap_first_entry(ld, res); msg;
msg = ldap_next_entry(ld, msg)) {
char *s = get_ldap_attr(ld, msg, attr);
if (s)
ret[i++] = s;
}
if (i != rc) {
argcv_free(i, ret);
ret = NULL;
}
ret[i] = NULL;
qsort(ret, i, sizeof(ret[0]), keycmp);
}
ldap_msgfree(res); ldap_msgfree(res);
return ret; return ret;
@ -968,7 +1007,7 @@ recursive_copy(pam_handle_t *pamh, DIR *dir,
if (chown(dstbuf->name, pw->pw_uid, pw->pw_gid) || if (chown(dstbuf->name, pw->pw_uid, pw->pw_gid) ||
(st && chmod(dstbuf->name, st->st_mode & 07777))) { (st && chmod(dstbuf->name, st->st_mode & 07777))) {
_pam_log(LOG_ERR, _pam_log(LOG_ERR,
"cannot set privileges of %s:" "cannot set privileges for %s:"
"%s", "%s",
dstbuf->name, dstbuf->name,
strerror(errno)); strerror(errno));
@ -1043,15 +1082,14 @@ populate_homedir(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env)
} }
static int static int
store_pubkey(const char *key, struct passwd *pw) store_pubkeys(char **keys, struct passwd *pw)
{ {
FILE *fp; FILE *fp;
const char *kp;
int c; int c;
int found = 0;
char *file_name; char *file_name;
size_t homelen, pathlen, len; size_t homelen, pathlen, len;
int retval; int retval, i;
int update = 0;
homelen = strlen(pw->pw_dir); homelen = strlen(pw->pw_dir);
pathlen = strlen(authorized_keys_file); pathlen = strlen(authorized_keys_file);
@ -1064,9 +1102,11 @@ store_pubkey(const char *key, struct passwd *pw)
file_name[homelen++] = '/'; file_name[homelen++] = '/';
strcpy(file_name + homelen, authorized_keys_file); strcpy(file_name + homelen, authorized_keys_file);
fp = fopen(file_name, "a+"); fp = fopen(file_name, "r+");
if (!fp && create_interdir(file_name, pw) == 0) if (!fp && create_interdir(file_name, pw) == 0) {
fp = fopen(file_name, "a+"); fp = fopen(file_name, "w");
update = 1;
}
if (!fp) { if (!fp) {
_pam_log(LOG_EMERG, "cannot open file %s: %s", _pam_log(LOG_EMERG, "cannot open file %s: %s",
file_name, strerror(errno)); file_name, strerror(errno));
@ -1076,32 +1116,35 @@ store_pubkey(const char *key, struct passwd *pw)
free(file_name); free(file_name);
fchown(fileno(fp), pw->pw_uid, pw->pw_gid); fchown(fileno(fp), pw->pw_uid, pw->pw_gid);
kp = key; if (!update) {
while (!feof(fp)) { i = 0;
do {
const char *kp = keys[i++];
if (!kp) {
DEBUG(2, ("some keys deleted"));
update = 1;
break;
}
while (*kp && (c = getc(fp)) != EOF && c == *kp) while (*kp && (c = getc(fp)) != EOF && c == *kp)
kp++; kp++;
if (*kp == 0) { if (*kp) {
DEBUG(2, ("key found")); DEBUG(2, ("key %d mismatch", i));
found = 1; update = 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; break;
} }
} while (c == '\n');
if (update) {
rewind(fp);
ftruncate(fileno(fp), 0);
} }
} }
if (!found) { if (update) {
fwrite(key, strlen(key), 1, fp); for (i = 0; keys[i]; i++) {
fwrite(keys[i], strlen(keys[i]), 1, fp);
fputc('\n', fp); fputc('\n', fp);
}
retval = PAM_TRY_AGAIN; retval = PAM_TRY_AGAIN;
} else } else
retval = PAM_SUCCESS; retval = PAM_SUCCESS;
@ -1135,17 +1178,17 @@ import_public_key(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env)
else { else {
char *filter; char *filter;
gray_slist_t slist; gray_slist_t slist;
char *pubkey; char **keys;
slist = gray_slist_create(); slist = gray_slist_create();
gray_expand_string(pamh, filter_pat, slist); gray_expand_string(pamh, filter_pat, slist);
gray_slist_append_char(slist, 0); gray_slist_append_char(slist, 0);
filter = gray_slist_finish(slist); filter = gray_slist_finish(slist);
pubkey = ldap_search(ld, base, filter, attr); keys = get_pubkeys(ld, base, filter, attr);
gray_slist_free(&slist); gray_slist_free(&slist);
retval = store_pubkey(pubkey, pw); retval = store_pubkeys(keys, pw);
free(pubkey); argcvz_free(keys);
} }
ldap_unbind(ld); ldap_unbind(ld);
return retval; return retval;