aboutsummaryrefslogtreecommitdiff
path: root/pam_ldaphome/pam_ldaphome.c
diff options
context:
space:
mode:
Diffstat (limited to 'pam_ldaphome/pam_ldaphome.c')
-rw-r--r--pam_ldaphome/pam_ldaphome.c306
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);

Return to:

Send suggestions and report system problems to the System administrator.