From 2bf454b74dc59e48c76f8d39c5fb2db4fc8605b8 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Wed, 27 May 2015 09:51:54 -0600 Subject: [PATCH] Use non-exiting allocators in the redblack tree and fix the fallout. Also switch to non-exiting allocators in affected code blocks. --- plugins/sudoers/alias.c | 11 +++- plugins/sudoers/gram.c | 27 ++++++--- plugins/sudoers/gram.y | 23 +++++-- plugins/sudoers/iolog.c | 6 +- plugins/sudoers/parse.h | 4 +- plugins/sudoers/pwutil.c | 110 ++++++++++++++++++++++++++++------ plugins/sudoers/redblack.c | 67 +++++++++++---------- plugins/sudoers/redblack.h | 2 +- plugins/sudoers/sudoers.c | 6 +- plugins/sudoers/sudoers.h | 4 +- plugins/sudoers/testsudoers.c | 4 +- plugins/sudoers/visudo.c | 11 +++- 12 files changed, 196 insertions(+), 79 deletions(-) diff --git a/plugins/sudoers/alias.c b/plugins/sudoers/alias.c index f68aa0540..138dfb632 100644 --- a/plugins/sudoers/alias.c +++ b/plugins/sudoers/alias.c @@ -132,10 +132,15 @@ alias_add(char *name, int type, struct member *members) a->type = type; /* a->used = false; */ HLTQ_TO_TAILQ(&a->members, members, entries); - if (rbinsert(aliases, a)) { + switch (rbinsert(aliases, a, NULL)) { + case 1: snprintf(errbuf, sizeof(errbuf), N_("Alias `%s' already defined"), name); alias_free(a); debug_return_str(errbuf); + case -1: + strlcpy(errbuf, N_("unable to allocate memory"), sizeof(errbuf)); + alias_free(a); + debug_return_str(errbuf); } debug_return_str(NULL); } @@ -209,7 +214,7 @@ alias_remove(char *name, int type) debug_return_ptr(rbdelete(aliases, node)); } -void +bool init_aliases(void) { debug_decl(init_aliases, SUDOERS_DEBUG_ALIAS) @@ -218,5 +223,5 @@ init_aliases(void) rbdestroy(aliases, alias_free); aliases = rbcreate(alias_compare); - debug_return; + debug_return_bool(aliases != NULL); } diff --git a/plugins/sudoers/gram.c b/plugins/sudoers/gram.c index 33e01e1fd..c6ace4ca2 100644 --- a/plugins/sudoers/gram.c +++ b/plugins/sudoers/gram.c @@ -844,12 +844,13 @@ add_userspec(struct member *members, struct privilege *privs) * Free up space used by data structures from a previous parser run and sets * the current sudoers file to path. */ -void +bool init_parser(const char *path, bool quiet) { struct member_list *binding; struct defaults *d, *d_next; struct userspec *us, *us_next; + bool rval = true; debug_decl(init_parser, SUDOERS_DEBUG_PARSER) TAILQ_FOREACH_SAFE(us, &userspecs, entries, us_next) { @@ -954,21 +955,31 @@ init_parser(const char *path, bool quiet) } TAILQ_INIT(&defaults); - init_aliases(); - init_lexer(); - sudo_efree(sudoers); - sudoers = path ? sudo_estrdup(path) : NULL; + if (!init_aliases()) { + sudo_warnx(U_("unable to allocate memory")); + rval = false; + } + + free(sudoers); + if (path != NULL) { + if ((sudoers = strdup(path)) == NULL) { + sudo_warnx(U_("unable to allocate memory")); + rval = false; + } + } else { + sudoers = NULL; + } parse_error = false; errorlineno = -1; errorfile = sudoers; sudoers_warnings = !quiet; - debug_return; + debug_return_bool(rval); } -#line 919 "gram.c" +#line 930 "gram.c" /* allocate initial stack or double stack size, up to YYMAXDEPTH */ #if defined(__cplusplus) || defined(__STDC__) static int yygrowstack(void) @@ -1877,7 +1888,7 @@ case 113: yyval.member = new_member(yyvsp[0].string, WORD); } break; -#line 1828 "gram.c" +#line 1839 "gram.c" } yyssp -= yym; yystate = *yyssp; diff --git a/plugins/sudoers/gram.y b/plugins/sudoers/gram.y index 30029fb16..f3260bf8e 100644 --- a/plugins/sudoers/gram.y +++ b/plugins/sudoers/gram.y @@ -818,12 +818,13 @@ add_userspec(struct member *members, struct privilege *privs) * Free up space used by data structures from a previous parser run and sets * the current sudoers file to path. */ -void +bool init_parser(const char *path, bool quiet) { struct member_list *binding; struct defaults *d, *d_next; struct userspec *us, *us_next; + bool rval = true; debug_decl(init_parser, SUDOERS_DEBUG_PARSER) TAILQ_FOREACH_SAFE(us, &userspecs, entries, us_next) { @@ -928,17 +929,27 @@ init_parser(const char *path, bool quiet) } TAILQ_INIT(&defaults); - init_aliases(); - init_lexer(); - sudo_efree(sudoers); - sudoers = path ? sudo_estrdup(path) : NULL; + if (!init_aliases()) { + sudo_warnx(U_("unable to allocate memory")); + rval = false; + } + + free(sudoers); + if (path != NULL) { + if ((sudoers = strdup(path)) == NULL) { + sudo_warnx(U_("unable to allocate memory")); + rval = false; + } + } else { + sudoers = NULL; + } parse_error = false; errorlineno = -1; errorfile = sudoers; sudoers_warnings = !quiet; - debug_return; + debug_return_bool(rval); } diff --git a/plugins/sudoers/iolog.c b/plugins/sudoers/iolog.c index 6aac46da1..380b45ec7 100644 --- a/plugins/sudoers/iolog.c +++ b/plugins/sudoers/iolog.c @@ -587,8 +587,10 @@ sudoers_io_open(unsigned int version, sudo_conv_t conversation, bindtextdomain("sudoers", LOCALEDIR); - sudo_setpwent(); - sudo_setgrent(); + if (sudo_setpwent() == -1 || sudo_setgrent() == -1) { + sudo_warnx(U_("unable to allocate memory")); + debug_return_int(-1); + } /* Initialize the debug subsystem. */ for (cur = settings; *cur != NULL; cur++) { diff --git a/plugins/sudoers/parse.h b/plugins/sudoers/parse.h index b2dca4956..e51d2326b 100644 --- a/plugins/sudoers/parse.h +++ b/plugins/sudoers/parse.h @@ -192,10 +192,10 @@ struct alias *alias_remove(char *name, int type); void alias_apply(int (*func)(void *, void *), void *cookie); void alias_free(void *a); void alias_put(struct alias *a); -void init_aliases(void); +bool init_aliases(void); /* gram.c */ -void init_parser(const char *, bool); +bool init_parser(const char *, bool); /* match_addr.c */ bool addr_matches(char *n); diff --git a/plugins/sudoers/pwutil.c b/plugins/sudoers/pwutil.c index f05a9a090..394bd63e5 100644 --- a/plugins/sudoers/pwutil.c +++ b/plugins/sudoers/pwutil.c @@ -139,16 +139,29 @@ sudo_getpwuid(uid_t uid) #endif item = sudo_make_pwitem(uid, NULL); if (item == NULL) { - item = sudo_ecalloc(1, sizeof(*item)); + item = calloc(1, sizeof(*item)); + if (item == NULL) { + sudo_warnx(U_("unable to cache uid %u, out of memory"), + (unsigned int) uid); + debug_return_ptr(NULL); + } item->refcnt = 1; item->k.uid = uid; /* item->d.pw = NULL; */ } - if (rbinsert(pwcache_byuid, item) != NULL) { + switch (rbinsert(pwcache_byuid, item, NULL)) { + case 1: /* should not happen */ sudo_warnx(U_("unable to cache uid %u, already exists"), (unsigned int) uid); item->refcnt = 0; + break; + case -1: + /* can't cache item, just return it */ + sudo_warnx(U_("unable to cache uid %u, out of memory"), + (unsigned int) uid); + item->refcnt = 0; + break; } #ifdef HAVE_SETAUTHDB aix_restoreauthdb(); @@ -183,16 +196,27 @@ sudo_getpwnam(const char *name) item = sudo_make_pwitem((uid_t)-1, name); if (item == NULL) { len = strlen(name) + 1; - item = sudo_ecalloc(1, sizeof(*item) + len); + item = calloc(1, sizeof(*item) + len); + if (item == NULL) { + sudo_warnx(U_("unable to cache user %s, out of memory"), name); + debug_return_ptr(NULL); + } item->refcnt = 1; item->k.name = (char *) item + sizeof(*item); memcpy(item->k.name, name, len); /* item->d.pw = NULL; */ } - if (rbinsert(pwcache_byname, item) != NULL) { + switch (rbinsert(pwcache_byname, item, NULL)) { + case 1: /* should not happen */ sudo_warnx(U_("unable to cache user %s, already exists"), name); item->refcnt = 0; + break; + case -1: + /* can't cache item, just return it */ + sudo_warnx(U_("unable to cache user %s, out of memory"), name); + item->refcnt = 0; + break; } #ifdef HAVE_SETAUTHDB aix_restoreauthdb(); @@ -253,15 +277,16 @@ sudo_mkpwent(const char *user, uid_t uid, gid_t gid, const char *home, item->refcnt = 1; item->d.pw = pw; if (i == 0) { - /* Store by uid if it doesn't already exist. */ + /* Store by uid. */ item->k.uid = pw->pw_uid; pwcache = pwcache_byuid; } else { - /* Store by name if it doesn't already exist. */ + /* Store by name. */ item->k.name = pw->pw_name; pwcache = pwcache_byname; } - if ((node = rbinsert(pwcache, item)) != NULL) { + switch (rbinsert(pwcache, item, &node)) { + case 1: /* Already exists. */ item = node->data; if (item->d.pw == NULL) { @@ -272,6 +297,12 @@ sudo_mkpwent(const char *user, uid_t uid, gid_t gid, const char *home, /* Good entry, discard our fake one. */ sudo_efree(pwitem); } + break; + case -1: + /* can't cache item, just return it */ + sudo_warnx(U_("unable to cache user %s, out of memory"), user); + item->refcnt = 0; + break; } } item->refcnt++; @@ -297,18 +328,21 @@ sudo_fakepwnam(const char *user, gid_t gid) debug_return_ptr(sudo_mkpwent(user, uid, gid, NULL, NULL)); } -void +int sudo_setpwent(void) { debug_decl(sudo_setpwent, SUDOERS_DEBUG_NSS) - setpwent(); if (pwcache_byuid == NULL) pwcache_byuid = rbcreate(cmp_pwuid); if (pwcache_byname == NULL) pwcache_byname = rbcreate(cmp_pwnam); + if (pwcache_byuid == NULL || pwcache_byname == NULL) + debug_return_int(-1); - debug_return; + setpwent(); + + debug_return_int(0); } void @@ -403,11 +437,19 @@ sudo_getgrgid(gid_t gid) item->k.gid = gid; /* item->d.gr = NULL; */ } - if (rbinsert(grcache_bygid, item) != NULL) { + switch (rbinsert(grcache_bygid, item, NULL)) { + case 1: /* should not happen */ sudo_warnx(U_("unable to cache gid %u, already exists"), (unsigned int) gid); item->refcnt = 0; + break; + case -1: + /* can't cache item, just return it */ + sudo_warnx(U_("unable to cache gid %u, out of memory"), + (unsigned int) gid); + item->refcnt = 0; + break; } done: item->refcnt++; @@ -442,10 +484,17 @@ sudo_getgrnam(const char *name) memcpy(item->k.name, name, len); /* item->d.gr = NULL; */ } - if (rbinsert(grcache_byname, item) != NULL) { + switch (rbinsert(grcache_byname, item, NULL)) { + case 1: /* should not happen */ sudo_warnx(U_("unable to cache group %s, already exists"), name); item->refcnt = 0; + break; + case -1: + /* can't cache item, just return it */ + sudo_warnx(U_("unable to cache group %s, out of memory"), name); + item->refcnt = 0; + break; } done: item->refcnt++; @@ -497,7 +546,8 @@ sudo_fakegrnam(const char *group) gritem->cache.k.name = gr->gr_name; grcache = grcache_byname; } - if ((node = rbinsert(grcache, item)) != NULL) { + switch (rbinsert(grcache, item, &node)) { + case 1: /* Already exists. */ item = node->data; if (item->d.gr == NULL) { @@ -508,6 +558,12 @@ sudo_fakegrnam(const char *group) /* Good entry, discard our fake one. */ sudo_efree(gritem); } + break; + case -1: + /* can't cache item, just return it */ + sudo_warnx(U_("unable to cache group %s, out of memory"), group); + item->refcnt = 0; + break; } } item->refcnt++; @@ -542,20 +598,23 @@ sudo_grlist_delref(struct group_list *grlist) debug_return; } -void +int sudo_setgrent(void) { debug_decl(sudo_setgrent, SUDOERS_DEBUG_NSS) - setgrent(); if (grcache_bygid == NULL) grcache_bygid = rbcreate(cmp_grgid); if (grcache_byname == NULL) grcache_byname = rbcreate(cmp_grnam); if (grlist_cache == NULL) grlist_cache = rbcreate(cmp_grnam); + if (grcache_bygid == NULL || grcache_byname == NULL || grlist_cache == NULL) + debug_return_int(-1); - debug_return; + setgrent(); + + debug_return_int(0); } void @@ -616,11 +675,19 @@ sudo_get_grlist(const struct passwd *pw) memcpy(item->k.name, pw->pw_name, len); /* item->d.grlist = NULL; */ } - if (rbinsert(grlist_cache, item) != NULL) { + switch (rbinsert(grlist_cache, item, NULL)) { + case 1: /* should not happen */ sudo_warnx(U_("unable to cache group list for %s, already exists"), pw->pw_name); item->refcnt = 0; + break; + case -1: + /* can't cache item, just return it */ + sudo_warnx(U_("unable to cache group list for %s, out of memory"), + pw->pw_name); + item->refcnt = 0; + break; } done: item->refcnt++; @@ -643,10 +710,17 @@ sudo_set_grlist(struct passwd *pw, char * const *groups, char * const *gids) sudo_warnx(U_("unable to parse groups for %s"), pw->pw_name); debug_return_int(-1); } - if (rbinsert(grlist_cache, item) != NULL) { + switch (rbinsert(grlist_cache, item, NULL)) { + case 1: sudo_warnx(U_("unable to cache group list for %s, already exists"), pw->pw_name); sudo_grlist_delref_item(item); + break; + case -1: + sudo_warnx(U_("unable to cache group list for %s, out of memory"), + pw->pw_name); + sudo_grlist_delref_item(item); + debug_return_int(-1); } } debug_return_int(0); diff --git a/plugins/sudoers/redblack.c b/plugins/sudoers/redblack.c index 28b62a497..789f8015e 100644 --- a/plugins/sudoers/redblack.c +++ b/plugins/sudoers/redblack.c @@ -82,7 +82,8 @@ static void rbdestroy_int(struct rbtree *, struct rbnode *, void (*)(void *)); /* * Create a red black tree struct using the specified compare routine. - * Allocates and returns the initialized (empty) tree. + * Allocates and returns the initialized (empty) tree or NULL if + * memory cannot be allocated. */ struct rbtree * rbcreate(int (*compar)(const void *, const void*)) @@ -90,24 +91,25 @@ rbcreate(int (*compar)(const void *, const void*)) struct rbtree *tree; debug_decl(rbcreate, SUDOERS_DEBUG_RBTREE) - tree = sudo_emalloc(sizeof(*tree)); - tree->compar = compar; - - /* - * We use a self-referencing sentinel node called nil to simplify the - * code by avoiding the need to check for NULL pointers. - */ - tree->nil.left = tree->nil.right = tree->nil.parent = &tree->nil; - tree->nil.color = black; - tree->nil.data = NULL; - - /* - * Similarly, the fake root node keeps us from having to worry - * about splitting the root. - */ - tree->root.left = tree->root.right = tree->root.parent = &tree->nil; - tree->root.color = black; - tree->root.data = NULL; + if ((tree = malloc(sizeof(*tree))) != NULL) { + tree->compar = compar; + + /* + * We use a self-referencing sentinel node called nil to simplify the + * code by avoiding the need to check for NULL pointers. + */ + tree->nil.left = tree->nil.right = tree->nil.parent = &tree->nil; + tree->nil.color = black; + tree->nil.data = NULL; + + /* + * Similarly, the fake root node keeps us from having to worry + * about splitting the root. + */ + tree->root.left = tree->root.right = tree->root.parent = &tree->nil; + tree->root.color = black; + tree->root.data = NULL; + } debug_return_ptr(tree); } @@ -166,11 +168,11 @@ rotate_right(struct rbtree *tree, struct rbnode *node) /* * Insert data pointer into a redblack tree. - * Returns a NULL pointer on success. If a node matching "data" - * already exists, a pointer to the existant node is returned. + * Returns a 0 on success, 1 if a node matching "data" already exists + * (filling in "existing" if not NULL), or -1 on malloc() failure. */ -struct rbnode * -rbinsert(struct rbtree *tree, void *data) +int +rbinsert(struct rbtree *tree, void *data, struct rbnode **existing) { struct rbnode *node = rbfirst(tree); struct rbnode *parent = rbroot(tree); @@ -180,12 +182,17 @@ rbinsert(struct rbtree *tree, void *data) /* Find correct insertion point. */ while (node != rbnil(tree)) { parent = node; - if ((res = tree->compar(data, node->data)) == 0) - debug_return_ptr(node); + if ((res = tree->compar(data, node->data)) == 0) { + if (existing != NULL) + *existing = node; + debug_return_int(1); + } node = res < 0 ? node->left : node->right; } - node = sudo_emalloc(sizeof(*node)); + node = malloc(sizeof(*node)); + if (node == NULL) + debug_return_int(-1); node->data = data; node->left = node->right = rbnil(tree); node->parent = parent; @@ -255,7 +262,7 @@ rbinsert(struct rbtree *tree, void *data) } } rbfirst(tree)->color = black; /* first node is always black */ - debug_return_ptr(NULL); + debug_return_int(0); } /* @@ -341,13 +348,13 @@ rbdestroy_int(struct rbtree *tree, struct rbnode *node, void (*destroy)(void *)) rbdestroy_int(tree, node->right, destroy); if (destroy != NULL) destroy(node->data); - sudo_efree(node); + free(node); } debug_return; } /* - * Destroy the specified tree, calling the destructor destroy + * Destroy the specified tree, calling the destructor "destroy" * for each node and then freeing the tree itself. */ void @@ -355,7 +362,7 @@ rbdestroy(struct rbtree *tree, void (*destroy)(void *)) { debug_decl(rbdestroy, SUDOERS_DEBUG_RBTREE) rbdestroy_int(tree, rbfirst(tree), destroy); - sudo_efree(tree); + free(tree); debug_return; } diff --git a/plugins/sudoers/redblack.h b/plugins/sudoers/redblack.h index cc2103cb8..5f5a78ce1 100644 --- a/plugins/sudoers/redblack.h +++ b/plugins/sudoers/redblack.h @@ -51,7 +51,7 @@ void *rbdelete(struct rbtree *, struct rbnode *); int rbapply_node(struct rbtree *, struct rbnode *, int (*)(void *, void *), void *, enum rbtraversal); struct rbnode *rbfind(struct rbtree *, void *); -struct rbnode *rbinsert(struct rbtree *, void *); +int rbinsert(struct rbtree *, void *, struct rbnode **); struct rbtree *rbcreate(int (*)(const void *, const void *)); void rbdestroy(struct rbtree *, void (*)(void *)); diff --git a/plugins/sudoers/sudoers.c b/plugins/sudoers/sudoers.c index e59075a93..0a6195ef5 100644 --- a/plugins/sudoers/sudoers.c +++ b/plugins/sudoers/sudoers.c @@ -124,8 +124,10 @@ sudoers_policy_init(void *info, char * const envp[]) bindtextdomain("sudoers", LOCALEDIR); - sudo_setpwent(); - sudo_setgrent(); + if (sudo_setpwent() == -1 || sudo_setgrent() == -1) { + sudo_warnx(U_("unable to allocate memory")); + debug_return_int(-1); + } /* Register fatal/fatalx callback. */ sudo_fatal_callback_register(sudoers_cleanup); diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h index 72e14498e..de4e87f3f 100644 --- a/plugins/sudoers/sudoers.h +++ b/plugins/sudoers/sudoers.h @@ -303,8 +303,8 @@ void sudo_grlist_delref(struct group_list *); void sudo_pw_addref(struct passwd *); void sudo_pw_delref(struct passwd *); int sudo_set_grlist(struct passwd *pw, char * const *groups, char * const *gids); -void sudo_setgrent(void); -void sudo_setpwent(void); +int sudo_setgrent(void); +int sudo_setpwent(void); void sudo_setspent(void); /* timestr.c */ diff --git a/plugins/sudoers/testsudoers.c b/plugins/sudoers/testsudoers.c index 72b2d2b5a..569b7e3d3 100644 --- a/plugins/sudoers/testsudoers.c +++ b/plugins/sudoers/testsudoers.c @@ -193,8 +193,8 @@ main(int argc, char *argv[]) setgrfile(grfile); if (pwfile) setpwfile(pwfile); - sudo_setpwent(); - sudo_setgrent(); + if (sudo_setpwent() == -1 || sudo_setgrent() == -1) + sudo_fatalx(U_("unable to allocate memory")); if (argc < 2) { if (!dflag) diff --git a/plugins/sudoers/visudo.c b/plugins/sudoers/visudo.c index ac651d0c3..2f65f2c31 100644 --- a/plugins/sudoers/visudo.c +++ b/plugins/sudoers/visudo.c @@ -222,8 +222,8 @@ main(int argc, char *argv[]) if (argc - optind != 0) usage(1); - sudo_setpwent(); - sudo_setgrent(); + if (sudo_setpwent() == -1 || sudo_setgrent() == -1) + sudo_fatalx(U_("unable to allocate memory")); /* Mock up a fake sudo_user struct. */ user_cmnd = user_base = ""; @@ -1106,7 +1106,8 @@ alias_remove_recursive(char *name, int type) rval = false; } } - rbinsert(alias_freelist, a); + if (rbinsert(alias_freelist, a, NULL) != 0) + rval = false; } debug_return_bool(rval); } @@ -1172,6 +1173,10 @@ check_aliases(bool strict, bool quiet) debug_decl(check_aliases, SUDOERS_DEBUG_ALIAS) alias_freelist = rbcreate(alias_compare); + if (alias_freelist == NULL) { + sudo_warnx(U_("unable to allocate memory")); + debug_return_int(-1); + } /* Forward check. */ TAILQ_FOREACH(us, &userspecs, entries) { -- 2.40.0