github.com/afumu/libc@v0.0.6/musl/src/passwd/getspnam_r.c (about)

     1  #include <fcntl.h>
     2  #include <unistd.h>
     3  #include <sys/stat.h>
     4  #include <ctype.h>
     5  #include <pthread.h>
     6  #include "pwf.h"
     7  
     8  /* This implementation support Openwall-style TCB passwords in place of
     9   * traditional shadow, if the appropriate directories and files exist.
    10   * Thus, it is careful to avoid following symlinks or blocking on fifos
    11   * which a malicious user might create in place of his or her TCB shadow
    12   * file. It also avoids any allocation to prevent memory-exhaustion
    13   * attacks via huge TCB shadow files. */
    14  
    15  static long xatol(char **s)
    16  {
    17  	long x;
    18  	if (**s == ':' || **s == '\n') return -1;
    19  	for (x=0; **s-'0'<10U; ++*s) x=10*x+(**s-'0');
    20  	return x;
    21  }
    22  
    23  int __parsespent(char *s, struct spwd *sp)
    24  {
    25  	sp->sp_namp = s;
    26  	if (!(s = strchr(s, ':'))) return -1;
    27  	*s = 0;
    28  
    29  	sp->sp_pwdp = ++s;
    30  	if (!(s = strchr(s, ':'))) return -1;
    31  	*s = 0;
    32  
    33  	s++; sp->sp_lstchg = xatol(&s);
    34  	if (*s != ':') return -1;
    35  
    36  	s++; sp->sp_min = xatol(&s);
    37  	if (*s != ':') return -1;
    38  
    39  	s++; sp->sp_max = xatol(&s);
    40  	if (*s != ':') return -1;
    41  
    42  	s++; sp->sp_warn = xatol(&s);
    43  	if (*s != ':') return -1;
    44  
    45  	s++; sp->sp_inact = xatol(&s);
    46  	if (*s != ':') return -1;
    47  
    48  	s++; sp->sp_expire = xatol(&s);
    49  	if (*s != ':') return -1;
    50  
    51  	s++; sp->sp_flag = xatol(&s);
    52  	if (*s != '\n') return -1;
    53  	return 0;
    54  }
    55  
    56  static void cleanup(void *p)
    57  {
    58  	fclose(p);
    59  }
    60  
    61  int getspnam_r(const char *name, struct spwd *sp, char *buf, size_t size, struct spwd **res)
    62  {
    63  	char path[20+NAME_MAX];
    64  	FILE *f = 0;
    65  	int rv = 0;
    66  	int fd;
    67  	size_t k, l = strlen(name);
    68  	int skip = 0;
    69  	int cs;
    70  	int orig_errno = errno;
    71  
    72  	*res = 0;
    73  
    74  	/* Disallow potentially-malicious user names */
    75  	if (*name=='.' || strchr(name, '/') || !l)
    76  		return errno = EINVAL;
    77  
    78  	/* Buffer size must at least be able to hold name, plus some.. */
    79  	if (size < l+100)
    80  		return errno = ERANGE;
    81  
    82  	/* Protect against truncation */
    83  	if (snprintf(path, sizeof path, "/etc/tcb/%s/shadow", name) >= sizeof path)
    84  		return errno = EINVAL;
    85  
    86  	fd = open(path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC);
    87  	if (fd >= 0) {
    88  		struct stat st = { 0 };
    89  		errno = EINVAL;
    90  		if (fstat(fd, &st) || !S_ISREG(st.st_mode) || !(f = fdopen(fd, "rb"))) {
    91  			pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
    92  			close(fd);
    93  			pthread_setcancelstate(cs, 0);
    94  			return errno;
    95  		}
    96  	} else {
    97  		if (errno != ENOENT && errno != ENOTDIR)
    98  			return errno;
    99  		f = fopen("/etc/shadow", "rbe");
   100  		if (!f) {
   101  			if (errno != ENOENT && errno != ENOTDIR)
   102  				return errno;
   103  			return 0;
   104  		}
   105  	}
   106  
   107  	pthread_cleanup_push(cleanup, f);
   108  	while (fgets(buf, size, f) && (k=strlen(buf))>0) {
   109  		if (skip || strncmp(name, buf, l) || buf[l]!=':') {
   110  			skip = buf[k-1] != '\n';
   111  			continue;
   112  		}
   113  		if (buf[k-1] != '\n') {
   114  			rv = ERANGE;
   115  			break;
   116  		}
   117  
   118  		if (__parsespent(buf, sp) < 0) continue;
   119  		*res = sp;
   120  		break;
   121  	}
   122  	pthread_cleanup_pop(1);
   123  	errno = rv ? rv : orig_errno;
   124  	return rv;
   125  }