diff options
Diffstat (limited to 'pam_ldaphome/pam_ldaphome.c')
-rw-r--r-- | pam_ldaphome/pam_ldaphome.c | 306 |
1 files changed, 191 insertions, 115 deletions
diff --git a/pam_ldaphome/pam_ldaphome.c b/pam_ldaphome/pam_ldaphome.c index dd3f9e3..0b492dd 100644 --- a/pam_ldaphome/pam_ldaphome.c +++ b/pam_ldaphome/pam_ldaphome.c @@ -174,13 +174,13 @@ get_intval(struct gray_env *env, const char *name, int base, unsigned long *pv) name); return -1; } return 0; } -char * +static char * parse_ldap_uri(const char *uri) { int wc; char **wv; LDAPURLDesc *ludlist, **ludp; char **urls = NULL; @@ -719,31 +719,41 @@ get_pubkeys(LDAP *ld, const char *base, const char *filter, const char *attr) static int check_groups(int gc, char **gv, const char *username, gid_t gid) { int i; struct group *gp; char *pgname; - + int rc = 1; + gp = getgrgid(gid); - pgname = gp ? gray_strdup(gp->gr_name) : NULL; - for (i = 0; i < gc; i++) { + if (gp) { + pgname = strdup(gp->gr_name); + if (!pgname) { + errno_to_pam(errno); + return -1; + } + } else + gp = NULL; + for (i = 0; rc && i < gc; i++) { if (strcmp(gv[i], pgname) == 0) { - free(pgname); - return 0; + rc = 0; + break; } gp = getgrnam(gv[i]); if (gp) { char **p; - for (p = gp->gr_mem; *p; p++) + for (p = gp->gr_mem; *p; p++) { if (strcmp(username, *p) == 0) { - free(pgname); - return 0; + rc = 0; + break; } + } } } - return 1; + free(pgname); + return rc; } static int check_user_groups(pam_handle_t *pamh, struct gray_env *env, struct passwd **ppw, int *retval) { @@ -853,81 +863,64 @@ copy_file(pam_handle_t *pamh, const char *src, const char *dst, close(sfd); close(dfd); return rc; } -#define INITIAL_READLINK_SIZE 128 - -int -read_link_name(const char *name, char **pbuf, size_t *psize, size_t *plen) +static int +read_link_name(const char *name, char **pbuf) { int rc = 0; - char *buf = *pbuf; - size_t size = *psize; - ssize_t linklen; + char *buf = NULL; + size_t size = 0; + ssize_t linklen = 0; while (1) { - if (!buf) { - size = INITIAL_READLINK_SIZE; - buf = malloc(size); - } else { - char *p; - size_t newsize = size << 1; - if (newsize < size) { - rc = ENAMETOOLONG; + if (linklen == size) { + char *p = gray_2nrealloc(&buf, &size, 1); + if (!p) { + errno_to_pam(errno); + rc = 1; break; } - size = newsize; - p = realloc(buf, size); - if (!p) - free(buf); buf = p; - } - if (!buf) { - rc = 1; - break; + linklen = size; } linklen = readlink(name, buf, size); if (linklen < 0 && errno != ERANGE) { rc = 1; break; } - if ((size_t) linklen < size) { + if (linklen < size) { buf[linklen++] = '\0'; rc = 0; break; } } if (rc) { if (buf) { free(buf); buf = NULL; } - size = 0; } *pbuf = buf; - *psize = size; - if (plen) - *plen = linklen; return rc; } static int copy_link(pam_handle_t *pamh, const char *src, const char *dst, char *buffer, size_t bufsize, struct stat *st) { - char *lnkname = NULL; - size_t lnklen = 0; + char *lnkname; int rc; - if (read_link_name(src, &lnkname, &lnklen, NULL)) { + if (read_link_name(src, &lnkname)) { _pam_log(LOG_ERR, "error reading link %s: %s", src, strerror(errno)); return 1; } rc = symlink(lnkname, dst); if (rc) @@ -996,13 +989,17 @@ create_interdir(const char *path, struct passwd *pw) int rc; p = strrchr(path, '/'); if (!p) return 1; len = p - path; - dir = gray_malloc(len + 1); + dir = malloc(len + 1); + if (!dir) { + errno_to_pam(errno); + return -1; + } memcpy(dir, path, len); dir[len] = 0; rc = create_hierarchy(dir, strlen(pw->pw_dir)); if (rc == 0) rc = chown(dir, pw->pw_uid, pw->pw_gid); free(dir); @@ -1265,25 +1262,46 @@ struct pubkeyfile { size_t size; /* Size of base */ char **lnv; /* File contents parsed into nul-terminated lines */ size_t lnc; /* Number of lines in lnv */ size_t lnm; /* Max. capacity of lnv */ }; +/* Close the public key file */ +static void +pubkeyfile_close(struct pubkeyfile *pkb) +{ + close(pkb->fd); + free(pkb->file_name); + free(pkb->base); + free(pkb->lnv); + + pkb->fd = -1; + pkb->file_name = NULL; + pkb->base = NULL; + pkb->lnv = NULL; +} + /* Open public key file NAME. Return 0 on success. On error, issue a diagnostic message and return -1. */ static int pubkeyfile_open(struct pubkeyfile *pkb, char *name) { memset(pkb, 0, sizeof *pkb); pkb->fd = open(name, O_CREAT|O_RDWR, 0666); if (pkb->fd == -1) { _pam_log(LOG_ERR, "can't open %s: %s", name, strerror(errno)); return -1; } - pkb->file_name = gray_strdup(name); + pkb->file_name = strdup(name); + if (!pkb->file_name) { + int rc = errno_to_pam(errno); + close(pkb->fd); + pkb->fd = -1; + return rc; + } return 0; } /* Read in the contents of the open public key file PKB. */ static int pubkeyfile_read(struct pubkeyfile *pkb) @@ -1295,26 +1313,33 @@ pubkeyfile_read(struct pubkeyfile *pkb) if (fstat(pkb->fd, &st)) { _pam_log(LOG_ERR, "fstat %s: %s", pkb->file_name, strerror(errno)); return -1; } pkb->size = st.st_size; - pkb->base = gray_malloc(st.st_size + 1); + pkb->base = malloc(st.st_size + 1); + if (!pkb->base) { + errno_to_pam(errno); + return -1; + } if (full_read(pkb->fd, pkb->file_name, pkb->base, pkb->size)) { _pam_log(LOG_ERR, "fread %s: %s", pkb->file_name, strerror(errno)); return -1; } pkb->base[pkb->size] = 0; pkb->lnc = 0; for (p = pkb->base; *p; p++) if (*p == '\n') ++pkb->lnc; pkb->lnm = pkb->lnc + 1; - pkb->lnv = gray_calloc(pkb->lnm, sizeof(pkb->lnv[0])); - + pkb->lnv = calloc(pkb->lnm, sizeof(pkb->lnv[0])); + if (pkb->lnv) { + errno_to_pam(errno); + return -1; + } i = 0; for (p = pkb->base; *p; p++) { if (p == pkb->base || p[-1] == 0) pkb->lnv[i++] = p; if (*p == '\n') *p = 0; @@ -1326,13 +1351,16 @@ pubkeyfile_read(struct pubkeyfile *pkb) /* Open the public key file NAME and read its contents. */ static int pubkeyfile_init(struct pubkeyfile *pkb, char *name) { if (pubkeyfile_open(pkb, name)) return -1; - return pubkeyfile_read(pkb); + if (pubkeyfile_read(pkb) == 0) + return 0; + pubkeyfile_close(pkb); + return -1; } /* Write data from lnv into the public key file, overwriting its current content. */ static int pubkeyfile_write(struct pubkeyfile *pkb) @@ -1385,57 +1413,54 @@ pubkeyfile_remove_lines(struct pubkeyfile *pkb, int pos, int count) (pkb->lnc - pos - count + 1) * sizeof(pkb->lnv[0])); pkb->lnc -= count; } /* Allocate COUNT lines starting from position POS in PKB, preserving the existing data. */ -static void +static int pubkeyfile_alloc_lines(struct pubkeyfile *pkb, size_t pos, size_t count) { if (pos > pkb->lnc) { _pam_log(LOG_ERR, "%s:%d: INTERNAL ERROR: pos out of range", __FILE__, __LINE__); - abort(); + return PAM_SERVICE_ERR; } if (pkb->lnc + count + 1 > pkb->lnm) { - pkb->lnm += count; - pkb->lnv = gray_realloc(pkb->lnv, - pkb->lnm * sizeof(pkb->lnv[0])); + char **p; + size_t lnm = pkb->lnm + count; + p = realloc(pkb->lnv, lnm * sizeof(pkb->lnv[0])); + if (!p) + return errno_to_pam(errno); + pkb->lnv = p; + pkb->lnm = lnm; } memmove(pkb->lnv + pos + count, pkb->lnv + pos, (pkb->lnc - pos + 1) * sizeof(pkb->lnv[0])); pkb->lnc += count; + return PAM_SUCCESS; } /* Insert lines from LV in position POS in the file PKB, shifting down existing lines as necessary. */ -void +static int pubkeyfile_insert_lines(struct pubkeyfile *pkb, size_t pos, char **lv) { size_t i; size_t lc; + int rc; for (lc = 0; lv[lc]; lc++) ; - pubkeyfile_alloc_lines(pkb, pos, lc); - - for (i = 0; i < lc; i++) - pkb->lnv[pos + i] = lv[i]; -} - -/* Close the public key file */ -void -pubkeyfile_close(struct pubkeyfile *pkb) -{ - close(pkb->fd); - free(pkb->file_name); - free(pkb->base); - free(pkb->lnv); + rc = pubkeyfile_alloc_lines(pkb, pos, lc); + if (rc == 0) { + for (i = 0; i < lc; i++) + pkb->lnv[pos + i] = lv[i]; + } + return rc; } - static int store_pubkeys(char **keys, struct passwd *pw, struct gray_env *env) { int rc; char *file_name; @@ -1448,13 +1473,15 @@ store_pubkeys(char **keys, struct passwd *pw, struct gray_env *env) 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); + file_name = malloc(len + 1); + if (!file_name) + return errno_to_pam(errno); 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)) { @@ -1517,15 +1544,17 @@ store_pubkeys(char **keys, struct passwd *pw, struct gray_env *env) update = 1; } } if (update) { pubkeyfile_remove_lines(&pkf, 0, i); - pubkeyfile_insert_lines(&pkf, 0, keys); - pubkeyfile_write(&pkf); - retval = PAM_TRY_AGAIN; + retval = pubkeyfile_insert_lines(&pkf, 0, keys); + if (retval == PAM_SUCCESS) { + pubkeyfile_write(&pkf); + retval = PAM_TRY_AGAIN; + } } else retval = PAM_SUCCESS; pubkeyfile_close(&pkf); free(file_name); return retval; @@ -1557,26 +1586,33 @@ import_public_key(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env) return PAM_SERVICE_ERR; if (ldap_bind(ld, env)) retval = PAM_SERVICE_ERR; else { char *filter; gray_slist_t slist; - char **keys; slist = gray_slist_create(); - 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); - if (keys) { - retval = store_pubkeys(keys, pw, env); - argcvz_free(keys); - } else - retval = PAM_SUCCESS; + if (!slist) + retval = errno_to_pam(errno); + else { + gray_expand_string(pamh, filter_pat, slist); + gray_slist_append_char(slist, 0); + filter = gray_slist_finish(slist); + if (filter) { + char **keys = get_pubkeys(ld, base, filter, + attr); + if (keys) { + retval = store_pubkeys(keys, pw, env); + argcvz_free(keys); + } else + retval = PAM_SUCCESS; + } else { + retval = errno_to_pam(gray_slist_err(slist)); + } + gray_slist_free(&slist); + } } ldap_unbind(ld); return retval; } static int @@ -1691,38 +1727,54 @@ locate_unset(char **env, const char *name) } } } return 0; } +/* Concatenate NAMELEN bytes from NAME, "=", A, and B. Allocate the + result using malloc. + + On ENOMEM, log a diagnostic message and exit with code 127. This function + should be used from child process only. +*/ static char * env_concat(char *name, size_t namelen, char *a, char *b) { char *res; size_t len; if (a && b) { - res = gray_malloc(namelen + 1 + strlen(a) + strlen(b) + 1); - strcpy(res + namelen + 1, a); - strcat(res, b); + res = malloc(namelen + 1 + strlen(a) + strlen(b) + 1); + if (res) { + strcpy(res + namelen + 1, a); + strcat(res, b); + } } else if (a) { len = strlen(a); if (ispunct(a[len-1])) len--; - res = gray_malloc(namelen + 1 + len + 1); - memcpy(res + namelen + 1, a, len); - res[namelen + 1 + len] = 0; + res = malloc(namelen + 1 + len + 1); + if (res) { + memcpy(res + namelen + 1, a, len); + res[namelen + 1 + len] = 0; + } } else /* if (a == NULL) */ { if (ispunct(b[0])) b++; len = strlen(b); - res = gray_malloc(namelen + 1 + len + 1); - strcpy(res + namelen + 1, b); + res = malloc(namelen + 1 + len + 1); + if (res) + strcpy(res + namelen + 1, b); } - memcpy(res, name, namelen); - res[namelen] = '='; + if (res) { + memcpy(res, name, namelen); + res[namelen] = '='; + } else { + errno_to_pam(errno); + _exit(127); + } return res; } static char ** parsenv(char *str) { @@ -1736,12 +1788,20 @@ parsenv(char *str) st_end } state = st_init, prev_state; # define setstate(s) do { prev_state = state; state = s; } while (0) char *p, *kw; char **wv = NULL; size_t wi = 0, wc = 0; + +# define ASSERT_NOTNULL(p) do { \ + if (!(p)) { \ + errno_to_pam(errno); \ + _exit(127); \ + } \ + } while (0) + if (!str) return NULL; for (p = str; *p; ++p) { switch (state) { @@ -1788,34 +1848,33 @@ parsenv(char *str) if (state == st_end) { size_t len = p - kw; char *q; if (wi == wc) { - if (wc == 0) - wc = 4; - else - wc *= 2; - wv = gray_realloc(wv, wc * sizeof(wv[0])); + wv = gray_2nrealloc(wv, &wc, sizeof(wv[0])); + ASSERT_NOTNULL(wv); } switch (prev_state) { case st_squote: len -= 2; - wv[wi] = gray_malloc(len + 1); + wv[wi] = malloc(len + 1); + ASSERT_NOTNULL(wv[wi]); for (q = wv[wi]; *kw; ) { if (*kw == '\'') ++kw; else *q++ = *kw++; } *q = 0; break; case st_dquote: len -= 2; - wv[wi] = gray_malloc(len + 1); + wv[wi] = malloc(len + 1); + ASSERT_NOTNULL(wv[wi]); q = wv[wi]; while ((*q++ = *kw++) != '=') ; while (*kw != '"') *q++ = *kw++; ++kw; @@ -1824,36 +1883,44 @@ parsenv(char *str) ++kw; *q++ = *kw++; } *q = 0; break; default: - wv[wi] = gray_malloc(len + 1); + wv[wi] = malloc(len + 1); + ASSERT_NOTNULL(wv[wi]); memcpy(wv[wi], kw, len); wv[wi][len] = 0; } ++wi; setstate(st_init); } } if (state != st_init) { if (wc == wi) { - ++wc; - wv = gray_realloc(wv, (wc + 1) * sizeof(wv[0])); + wv = gray_2nrealloc(wv, &wc, sizeof(wv[0])); + ASSERT_NOTNULL(wv); } - wv[wi++] = gray_strdup(kw); + wv[wi] = strdup(kw); + ASSERT_NOTNULL(wv[wi]); + ++wi; } - if (wc == wi) - wv = gray_realloc(wv, (wc + 1) * sizeof(wv[0])); + if (wc == wi) { + wv = gray_2nrealloc(wv, &wc, sizeof(wv[0])); + ASSERT_NOTNULL(wv); + } wv[wi] = NULL; return wv; } +/* Setup environment for exec* family call. On ENOMEM, exit with code 127. + This function should be called from a child process. +*/ static char ** env_setup(char *envstr) { char **env; char **old_env = environ; char **new_env; @@ -1876,14 +1943,17 @@ env_setup(char *envstr) count++; for (i = 0; env[i]; i++) count++; /* Allocate the new environment. */ - new_env = gray_calloc(count + 1, sizeof new_env[0]); - + new_env = calloc(count + 1, sizeof new_env[0]); + if (!new_env) { + errno_to_pam(errno); + _exit(127); + } /* Populate the environment. */ n = 0; if (old_env) for (i = 0; old_env[i]; i++) { if (!locate_unset(env, old_env[i])) @@ -1935,15 +2005,22 @@ runas(struct passwd *pw) while ((gr = getgrent ())) { char **p; if (gr->gr_gid == pw->pw_gid) continue; for (p = gr->gr_mem; *p; p++) { if (strcmp (*p, pw->pw_name) == 0) { - if (sgc == sgm) - sgv = gray_2nrealloc(sgv, &sgm, - sizeof(sgv[0])); + if (sgc == sgm) { + gid_t *p; + p = gray_2nrealloc(sgv, &sgm, + sizeof(sgv[0])); + if (!p) { + errno_to_pam(errno); + return 1; //FIXME: proper error code + } + sgv = p; + } sgv[sgc++] = gr->gr_gid; } } } endgrent(); @@ -2140,18 +2217,17 @@ ldaphome_main(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 %s", func)); - gray_pam_init(PAM_AUTHINFO_UNAVAIL); if (gray_env_read(config_file_name, &env) == 0) { char *val; struct passwd *pw; - if (val = gray_env_get(env, "ldap-config")) { + if ((val = gray_env_get(env, "ldap-config"))) { if (strcmp(val, "none") == 0) ldap_config_name = NULL; else ldap_config_name = val; } if (ldap_config_name) { @@ -2159,13 +2235,13 @@ ldaphome_main(pam_handle_t *pamh, int flags, int argc, const char **argv, struct gray_env *tmp; gray_env_read_tr(ldap_config_name, &tmp, map); gray_env_merge(&env, &tmp); } - if (val = gray_env_get(env, "authorized_keys")) + if ((val = gray_env_get(env, "authorized_keys"))) authorized_keys_file = val; if (check_user_groups(pamh, env, &pw, &retval) == 0) { switch (create_home_dir(pamh, pw, env)) { case create_ok: retval = run_initrc(pamh, pw, env); |