getgr_a.c raw

   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  
   8  static 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  
  20  int __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  
 160  cleanup_f:
 161  		fclose(f);
 162  		goto done;
 163  	}
 164  
 165  done:
 166  	pthread_setcancelstate(cs, 0);
 167  	if (rv) errno = rv;
 168  	return rv;
 169  }
 170