diff options
author | Michael Stone <michael@laptop.org> | 2009-09-30 02:22:48 (GMT) |
---|---|---|
committer | Michael Stone <michael@laptop.org> | 2009-09-30 05:53:20 (GMT) |
commit | 05156ce770ff4f7361fb0ccbbe946ac652666408 (patch) | |
tree | 3475d2821501c5cd31e59a9e5d1de0e880e81cc7 | |
parent | bb2f5070a469d96bbe2c084c27f1e808b1ef27e8 (diff) |
Make nss-rainbow's return and error codes more accurate.
-rw-r--r-- | rainbow/nss/cgen.h | 2 | ||||
-rw-r--r-- | rainbow/nss/nss-rainbow.c | 176 |
2 files changed, 86 insertions, 92 deletions
diff --git a/rainbow/nss/cgen.h b/rainbow/nss/cgen.h index 215dda0..5ef151a 100644 --- a/rainbow/nss/cgen.h +++ b/rainbow/nss/cgen.h @@ -5,4 +5,4 @@ #define PERROR(msg) #define CHK(EXPR, MSG, ERR_LABEL) {if(EXPR) { PERROR(MSG); goto ERR_LABEL;}} #define LET(LETEXPR, CONDEXPR, MSG, ERR_LABEL) LETEXPR; if (CONDEXPR) { PERROR(MSG); goto ERR_LABEL;} -#define TST(EXPR, ERRNO, MSG, ERR_LABEL) {if (EXPR) {errno = ERRNO; PERROR(MSG); goto ERR_LABEL;}} +#define TST(EXPR, TRUE, MSG, ERR_LABEL) {if (EXPR) {(TRUE); PERROR(MSG); goto ERR_LABEL;}} diff --git a/rainbow/nss/nss-rainbow.c b/rainbow/nss/nss-rainbow.c index 21d746a..5793cde 100644 --- a/rainbow/nss/nss-rainbow.c +++ b/rainbow/nss/nss-rainbow.c @@ -80,7 +80,7 @@ read_gid_for_uid(unsigned long uid, unsigned long* gid) { "Unable to read gid-path.", out_err); CHK(parse_nat(&val, gid_name, gid_name_len) == 1, "Unable to parse gid_buf into a gid.", out_err); - CHK(val < 10000 || val > 60000, + CHK(check_gid_in_range(val) == 1, "gid not in valid range.", out_err); *gid = val; @@ -91,17 +91,23 @@ out_err: enum nss_status _nss_rainbow_getpwent_r(struct passwd *result, char* buf, size_t buflen, int *errnop) { - /* I'm going to interpret the reentrancy requirement on this function to mean - * that the sequence of all results returned by all calls to this function - * between calls to setpwent() should contain contain every entry exactly - * once. It is undefined whether writes to the underlying storage that - * interleave with calls to getpwent() will be visible to callers. */ + /* Getting errno and the return code correct in this function is a pain + * because we need to return + * + * NSS_STATUS_TRYAGAIN + ERANGE if we run out of space in buf + * NSS_STATUS_NOTFOUND + ENOENT if we run out of uids + * NSS_STATUS_SUCCESS + _____, if we win + * or try another uid, if we lose + */ begin: + errno = 0; + /* Locate the next reserved uid. */ while (true){ g_uid++; - if (!g_uid) return NSS_STATUS_UNAVAIL; + TST(!g_uid, errno = ENOENT, + "No more uids to check.", out_error); if (g_uids[g_uid]) break; } @@ -114,22 +120,27 @@ begin: result->pw_dir = buf; CHK(format_buf(&buf, &buflen, SPOOL "/uid_to_home_dir/%d", g_uid) == 1, - "Unable to calculate home dir.", begin); + "Unable to calculate home dir.", maybe_error); result->pw_gecos = result->pw_name = result->pw_passwd = buf; CHK(format_buf(&buf, &buflen, "%d", g_uid) == 1, - "Not enough buffer for $USER.", begin); + "Not enough buffer for $USER.", maybe_error); result->pw_shell = buf; CHK(write_buf(&buf, &buflen, SHELL) == 1, - "Shell string constant too long.", begin); - - if(getenv(DEBUG) != NULL) - dprintf(2, "%s success: %s (%d, %d) %s %s %s\n", __func__, - result->pw_name, result->pw_uid, result->pw_gid, result->pw_dir, - result->pw_shell, result->pw_passwd); + "Shell string constant too long.", maybe_error); return NSS_STATUS_SUCCESS; + +maybe_error: + if(errno == ERANGE) goto out_error; + goto begin; + +out_error: + *errnop = errno; + if (errno == ERANGE) return NSS_STATUS_TRYAGAIN; + if (errno == ENOENT) return NSS_STATUS_NOTFOUND; + return NSS_STATUS_UNAVAIL; } enum nss_status _nss_rainbow_getpwuid_r(uid_t uid, struct passwd *result, char* buf, size_t buflen, int *errnop) { @@ -158,11 +169,6 @@ enum nss_status _nss_rainbow_getpwuid_r(uid_t uid, struct passwd *result, char* CHK(write_buf(&buf, &buflen, SHELL) == 1, "Shell string constant too long.", out_error_errno); - if(getenv(DEBUG) != NULL) - dprintf(2, "%s success in uid: %s (%d, %d) %s %s %s\n", __func__, - result->pw_name, result->pw_uid, result->pw_gid, - result->pw_dir, result->pw_shell, result->pw_passwd); - return NSS_STATUS_SUCCESS; out_error_errno: @@ -175,7 +181,7 @@ enum nss_status _nss_rainbow_getpwnam_r(const char * name, struct passwd *result CHK(parse_nat(&val, name, strlen(name)) == 1, "Unable to parse name into a uid.", out_error_name); - TST(val < 10000 || val > 60000, EINVAL, + TST(val < 10000 || val > 60000, errno = EINVAL, "Name specifies a uid outside [10000, 60000].", out_error_name); return _nss_rainbow_getpwuid_r((uid_t)val, result, buf, buflen, errnop); @@ -185,36 +191,6 @@ out_error_name: return NSS_STATUS_NOTFOUND; } -#if 0 -enum nss_status -_nss_rainbow_initgroups_dyn(const char *user, gid_t gid, long int *start, long int *size, gid_t **groupsp, long int limit, int *errnop) { - if (uid < 10000) - return NSS_STATUS_NOTFOUND; - - int pathlen = 1024; - char pathdata[1024], *pathbuf = pathdata; - - result->pw_dir = buf; - CHK(format_buf(&pathbuf, &pathlen, SPOOL "/uid_to_home_dir/%d", uid) == 1, - "Unable to calculate home dir.", out_error_errno); - - struct stat st; - CHK(stat(result->pw_dir, &st) == -1, - "Stat failed for homebuf.", out_error_errno); - result->pw_uid = uid; - result->pw_gid = st.st_gid; - - (*groupsp)[*start] = - - return NSS_STATUS_SUCCESS; - -out_error_errno: - *ernnop = errno; - return NSS_STATUS_NOTFOUND; -} - -#endif - struct member_list { struct list_head list; char* name; @@ -231,7 +207,7 @@ struct gid_list* g_gid; int members_scanner(const struct dirent* d) { size_t member_len = strlen(d->d_name); - char* member_str = d->d_name; + const char* member_str = d->d_name; CHK(member_len == 0 || (member_len == 1 && d->d_name[0] == '.') @@ -307,10 +283,10 @@ enum nss_status _nss_rainbow_setgrent(void) { INIT_LIST_HEAD(&g_gids.members.list); g_gid = &g_gids; CHK(scandir(SPOOL "/gid_to_members", &ents, gid_to_members_scanner, versionsort) == -1, - "Unable to extract gid->members dirs from $RAINBOW_SPOOL.", out_error_spool); + "Unable to extract gid->members dirs from $RAINBOW_SPOOL.", out_error); g_gid = list_entry(g_gids.list.next, struct gid_list, list); return NSS_STATUS_SUCCESS; -out_error_spool: +out_error: return NSS_STATUS_UNAVAIL; } @@ -341,12 +317,12 @@ struct group */ int -fill_group(struct group* result, char* buf, size_t buflen, int *errnop, struct gid_list* gid_iter) { +fill_group(struct group* result, char* buf, size_t buflen, struct gid_list* gid_iter) { result->gr_gid = gid_iter->gid; result->gr_name = result->gr_passwd = buf; - TST(format_buf(&buf, &buflen, "%d", gid_iter->gid) == 1, ENOMEM, - "Unable to calculate group name.", out_error_errno); + CHK(format_buf(&buf, &buflen, "%d", gid_iter->gid) == 1, + "Unable to calculate group name.", out_error); /* count the number of members */ struct member_list* member_iter; @@ -358,9 +334,9 @@ fill_group(struct group* result, char* buf, size_t buflen, int *errnop, struct g /* reserve space for the member-pointers */ size_t member_size = (member_count+1)* sizeof(char*); - result->gr_mem = buf; - TST(buflen < member_size, EINVAL, - "Not enough space for group member pointers.", out_error_errno); + result->gr_mem = (char**)buf; + TST(buflen < member_size, errno = ERANGE, + "Not enough space for group member pointers.", out_error); result->gr_mem[member_count] = 0; buf += member_size; buflen -= member_size; @@ -370,23 +346,22 @@ fill_group(struct group* result, char* buf, size_t buflen, int *errnop, struct g list_for_each_entry(member_iter, &(gid_iter->members.list), list) { result->gr_mem[member_count] = buf; CHK(format_buf(&buf, &buflen, "%s", member_iter->name) == 1, - "Unable to write a group member.", out_error_errno); + "Unable to write a group member.", out_error); member_count++; } return 0; -out_error_errno: - *errnop = errno; +out_error: return 1; } enum nss_status _nss_rainbow_getgrent_r(struct group *result, char *buf, size_t buflen, int *errnop) { if (&g_gid->list == &g_gids.list) - return NSS_STATUS_UNAVAIL; + return NSS_STATUS_NOTFOUND; - CHK(fill_group(result, buf, buflen, errnop, g_gid) == 1, + CHK(fill_group(result, buf, buflen, g_gid) == 1, "Unable to fill in group struct.", out_error_errno); g_gid = list_entry(g_gid->list.next, struct gid_list, list); @@ -397,58 +372,77 @@ out_error_errno: return NSS_STATUS_TRYAGAIN; } +int check_gid_in_range(gid_t gid) +{ + TST(gid < 10000 || gid > 60000, errno = ENOENT, + "Gid outside [10000, 60000].", out_error); + return 0; +out_error: + return 1; +} enum nss_status _nss_rainbow_getgrgid_r(gid_t gid, struct group *result, char *buf, size_t buflen, int *errnop) { - enum nss_status ret; - if (gid < 10000 || gid > 60000) - return NSS_STATUS_NOTFOUND; + /* Getting errno and the return code exactly right is a pain. + * The problem is that we need + * + * NSS_STATUS_NOTFOUND + ENOENT if we can't find the result + * NSS_STATUS_TRYAGAIN + ERANGE if buf isn't big enough + * NSS_STATUS_??? + ____ if we can't setgrent() + * NSS_STATUS_NOTFOUND + ____ if we have bogus data + * NSS_SUCCESS + ____ otherwise. + */ - /* XXX CHECK RETURN CODE */ - _nss_rainbow_setgrent(); + enum nss_status ret, tmp; + + LET(ret = _nss_rainbow_setgrent(), ret != NSS_STATUS_SUCCESS, + "Unable to setgrent().", out_error); + + TST(check_gid_in_range(gid) == 1, ret = NSS_STATUS_NOTFOUND, + "Gid outside [10000, 60000].", out_error); /* look up group data */ struct gid_list* gid_iter = NULL; list_for_each_entry(gid_iter, &g_gids.list, list) { - if (gid_iter->gid == gid) - break; - } - if (&gid_iter->list == &g_gids.list) - { - ret = NSS_STATUS_NOTFOUND; - goto out_dealloc; + if (gid_iter->gid == gid) break; } - if(fill_group(result, buf, buflen, errnop, gid_iter) == 1) - { - PERROR("Unable to fill in group struct."); - goto out_error; - } + TST(&gid_iter->list == &g_gids.list, ret = NSS_STATUS_NOTFOUND, + "Gid not found.", out_dealloc); - if(getenv(DEBUG) != NULL) - dprintf(2, "%s success in gid: %s (%d) %s\n", __func__, - result->gr_name, result->gr_gid, result->gr_passwd); + CHK(fill_group(result, buf, buflen, gid_iter) == 1, + "Unable to fill in group struct.", maybe_range); ret = NSS_STATUS_SUCCESS; goto out_dealloc; +maybe_range: + ret = (errno == ERANGE) ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; + out_error: *errnop = errno; + out_dealloc: - /* XXX CHECK RETURN CODE */ - _nss_rainbow_endgrent(); + tmp = _nss_rainbow_endgrent(); + TST(ret == NSS_STATUS_SUCCESS && tmp != NSS_STATUS_SUCCESS, ret = tmp, + "Unable to deallocate internal group data.", out); + +out: return ret; } enum nss_status _nss_rainbow_getgrnam_r(const char* name, struct group *result, char *buf, size_t buflen, int *errnop) { unsigned long val; + CHK(parse_nat(&val, name, strlen(name)) == 1, - "Unable to parse name into a gid.", out_error_name); - TST(val < 10000 || val > 60000, EINVAL, - "Name specifies a gid outside [10000, 60000].", out_error_name); + "Unable to parse name into a gid.", out_error); + + CHK(check_gid_in_range(val) == 1, + "Name specifies a gid outside [10000, 60000].", out_error); + return _nss_rainbow_getgrgid_r((gid_t)val, result, buf, buflen, errnop); -out_error_name: +out_error: *errnop = errno; return NSS_STATUS_NOTFOUND; } |