1#include <pthread.h> 2#include <byteswap.h> 3#include <string.h> 4#include <unistd.h> 5#include "pwf.h" 6#include "nscd.h" 7 8static char *itoa(char *p, uint32_t x) 9{ 10 // number of digits in a uint32_t + NUL 11 p += 11; 12 *--p = 0; 13 do { 14 *--p = '0' + x % 10; 15 x /= 10; 16 } while (x); 17 return p; 18} 19 20int __getgr_a(const char *name, gid_t gid, struct group *gr, char **buf, size_t *size, char ***mem, size_t *nmem, struct group **res) 21{ 22 FILE *f; 23 int rv = 0; 24 int cs; 25 26 *res = 0; 27 28 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); 29 f = fopen("/etc/group", "rbe"); 30 if (!f) { 31 rv = errno; 32 goto done; 33 } 34 35 while (!(rv = __getgrent_a(f, gr, buf, size, mem, nmem, res)) && *res) { 36 if (name && !strcmp(name, (*res)->gr_name) 37 || !name && (*res)->gr_gid == gid) { 38 break; 39 } 40 } 41 fclose(f); 42 43 if (!*res && (rv == 0 || rv == ENOENT || rv == ENOTDIR)) { 44 int32_t req = name ? GETGRBYNAME : GETGRBYGID; 45 int32_t i; 46 const char *key; 47 int32_t groupbuf[GR_LEN] = {0}; 48 size_t len = 0; 49 size_t grlist_len = 0; 50 char gidbuf[11] = {0}; 51 int swap = 0; 52 char *ptr; 53 54 if (name) { 55 key = name; 56 } else { 57 if (gid < 0 || gid > UINT32_MAX) { 58 rv = 0; 59 goto done; 60 } 61 key = itoa(gidbuf, gid); 62 } 63 64 f = __nscd_query(req, key, groupbuf, sizeof groupbuf, &swap); 65 if (!f) { rv = errno; goto done; } 66 67 if (!groupbuf[GRFOUND]) { rv = 0; goto cleanup_f; } 68 69 if (!groupbuf[GRNAMELEN] || !groupbuf[GRPASSWDLEN]) { 70 rv = EIO; 71 goto cleanup_f; 72 } 73 74 if (groupbuf[GRNAMELEN] > SIZE_MAX - groupbuf[GRPASSWDLEN]) { 75 rv = ENOMEM; 76 goto cleanup_f; 77 } 78 len = groupbuf[GRNAMELEN] + groupbuf[GRPASSWDLEN]; 79 80 for (i = 0; i < groupbuf[GRMEMCNT]; i++) { 81 uint32_t name_len; 82 if (fread(&name_len, sizeof name_len, 1, f) < 1) { 83 rv = ferror(f) ? errno : EIO; 84 goto cleanup_f; 85 } 86 if (swap) { 87 name_len = bswap_32(name_len); 88 } 89 if (name_len > SIZE_MAX - grlist_len 90 || name_len > SIZE_MAX - len) { 91 rv = ENOMEM; 92 goto cleanup_f; 93 } 94 len += name_len; 95 grlist_len += name_len; 96 } 97 98 if (len > *size || !*buf) { 99 char *tmp = realloc(*buf, len); 100 if (!tmp) { 101 rv = errno; 102 goto cleanup_f; 103 } 104 *buf = tmp; 105 *size = len; 106 } 107 108 if (!fread(*buf, len, 1, f)) { 109 rv = ferror(f) ? errno : EIO; 110 goto cleanup_f; 111 } 112 113 if (groupbuf[GRMEMCNT] + 1 > *nmem) { 114 if (groupbuf[GRMEMCNT] + 1 > SIZE_MAX/sizeof(char*)) { 115 rv = ENOMEM; 116 goto cleanup_f; 117 } 118 char **tmp = realloc(*mem, (groupbuf[GRMEMCNT]+1)*sizeof(char*)); 119 if (!tmp) { 120 rv = errno; 121 goto cleanup_f; 122 } 123 *mem = tmp; 124 *nmem = groupbuf[GRMEMCNT] + 1; 125 } 126 127 if (groupbuf[GRMEMCNT]) { 128 mem[0][0] = *buf + groupbuf[GRNAMELEN] + groupbuf[GRPASSWDLEN]; 129 for (ptr = mem[0][0], i = 0; ptr != mem[0][0]+grlist_len; ptr++) 130 if (!*ptr) mem[0][++i] = ptr+1; 131 mem[0][i] = 0; 132 133 if (i != groupbuf[GRMEMCNT]) { 134 rv = EIO; 135 goto cleanup_f; 136 } 137 } else { 138 mem[0][0] = 0; 139 } 140 141 gr->gr_name = *buf; 142 gr->gr_passwd = gr->gr_name + groupbuf[GRNAMELEN]; 143 gr->gr_gid = groupbuf[GRGID]; 144 gr->gr_mem = *mem; 145 146 if (gr->gr_passwd[-1] 147 || gr->gr_passwd[groupbuf[GRPASSWDLEN]-1]) { 148 rv = EIO; 149 goto cleanup_f; 150 } 151 152 if (name && strcmp(name, gr->gr_name) 153 || !name && gid != gr->gr_gid) { 154 rv = EIO; 155 goto cleanup_f; 156 } 157 158 *res = gr; 159 160cleanup_f: 161 fclose(f); 162 goto done; 163 } 164 165done: 166 pthread_setcancelstate(cs, 0); 167 if (rv) errno = rv; 168 return rv; 169} 170