/** * @file * * @brief User Database Access Routines * @ingroup libcsupport */ /* * Copyright (c) 1999-2009 Ralf Corsepius * Copyright (c) 1999-2013 Joel Sherrill * Copyright (c) 2000-2001 Fernando Ruiz Casas * Copyright (c) 2002 Eric Norum * Copyright (c) 2003 Till Straumann * Copyright (c) 2012 Alex Ivanov * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "pwdgrp.h" static pthread_once_t pwdgrp_once = PTHREAD_ONCE_INIT; static void init_file(const char *name, const char *content) { /* * Unlike to standard UNIX systems, these files are only readable and * writeable for the root user. This way we avoid the need for an * /etc/shadow. In case more UNIX compatibility is desired, this can be * added on demand. */ int fd = open(name, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR); if (fd >= 0) { write(fd, content, strlen(content)); close(fd); } } /** * Initialize useable but dummy databases */ static void pwdgrp_init(void) { int rc; rc = mkdir("/etc", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); if ( rc != 0 ) return; /* * Initialize /etc/passwd */ init_file("/etc/passwd", "root::0:0::::\n"); /* * Initialize /etc/group */ init_file("/etc/group", "root::0:\n"); } void _libcsupport_pwdgrp_init(void) { pthread_once(&pwdgrp_once, pwdgrp_init); } /** * Extract a string value from the database */ static int scanString(FILE *fp, char **name, char **bufp, size_t *nleft, int nlFlag) { int c; *name = *bufp; for (;;) { c = getc(fp); if (c == ':') { if (nlFlag) return 0; break; } if (c == '\n') { if (!nlFlag) return 0; break; } if (c == EOF) return 0; if (*nleft < 2) return 0; **bufp = c; ++(*bufp); --(*nleft); } **bufp = '\0'; ++(*bufp); --(*nleft); return 1; } /** * Extract an integer value from the database */ static int scanInt(FILE *fp, int *val) { int c; unsigned int i = 0; unsigned int limit = INT_MAX; int sign = 0; int d; for (;;) { c = getc(fp); if (c == ':') break; if (sign == 0) { if (c == '-') { sign = -1; limit++; continue; } sign = 1; } if (!isdigit(c)) return 0; d = c - '0'; if ((i > (limit / 10)) || ((i == (limit / 10)) && (d > (limit % 10)))) return 0; i = i * 10 + d; } if (sign == 0) return 0; *val = i * sign; return 1; } /* * Extract a single password record from the database */ int _libcsupport_scanpw( FILE *fp, struct passwd *pwd, char *buffer, size_t bufsize ) { int pwuid, pwgid; if (!scanString(fp, &pwd->pw_name, &buffer, &bufsize, 0) || !scanString(fp, &pwd->pw_passwd, &buffer, &bufsize, 0) || !scanInt(fp, &pwuid) || !scanInt(fp, &pwgid) || !scanString(fp, &pwd->pw_comment, &buffer, &bufsize, 0) || !scanString(fp, &pwd->pw_gecos, &buffer, &bufsize, 0) || !scanString(fp, &pwd->pw_dir, &buffer, &bufsize, 0) || !scanString(fp, &pwd->pw_shell, &buffer, &bufsize, 1)) return 0; pwd->pw_uid = pwuid; pwd->pw_gid = pwgid; return 1; } static int getpw_r( const char *name, int uid, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result ) { FILE *fp; int match; _libcsupport_pwdgrp_init(); if ((fp = fopen("/etc/passwd", "r")) == NULL) rtems_set_errno_and_return_minus_one( EINVAL ); for(;;) { if (!_libcsupport_scanpw(fp, pwd, buffer, bufsize)) goto error_einval; if (name) { match = (strcmp(pwd->pw_name, name) == 0); } else { match = (pwd->pw_uid == uid); } if (match) { fclose(fp); *result = pwd; return 0; } } error_einval: fclose(fp); rtems_set_errno_and_return_minus_one( EINVAL ); } int getpwnam_r( const char *name, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result ) { return getpw_r(name, 0, pwd, buffer, bufsize, result); } int getpwuid_r( uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result ) { return getpw_r(NULL, uid, pwd, buffer, bufsize, result); } /** * Extract a single group record from the database */ int _libcsupport_scangr( FILE *fp, struct group *grp, char *buffer, size_t bufsize ) { int grgid; char *grmem, *cp; int memcount; if (!scanString(fp, &grp->gr_name, &buffer, &bufsize, 0) || !scanString(fp, &grp->gr_passwd, &buffer, &bufsize, 0) || !scanInt(fp, &grgid) || !scanString(fp, &grmem, &buffer, &bufsize, 1)) return 0; grp->gr_gid = grgid; /* * Determine number of members */ if (grmem[0] == '\0') { memcount = 0; } else { for (cp = grmem, memcount = 1 ; *cp != 0 ; cp++) { if(*cp == ',') memcount++; } } /* * Hack to produce (hopefully) a suitably-aligned array of pointers */ if (bufsize < (((memcount+1)*sizeof(char *)) + 15)) return 0; grp->gr_mem = (char **)(((uintptr_t)buffer + 15) & ~15); /* * Fill in pointer array */ if (grmem[0] == '\0') { memcount = 0; } else { grp->gr_mem[0] = grmem; for (cp = grmem, memcount = 1 ; *cp != 0 ; cp++) { if(*cp == ',') { *cp = '\0'; grp->gr_mem[memcount++] = cp + 1; } } } grp->gr_mem[memcount] = NULL; return 1; } static int getgr_r( const char *name, int gid, struct group *grp, char *buffer, size_t bufsize, struct group **result ) { FILE *fp; int match; _libcsupport_pwdgrp_init(); if ((fp = fopen("/etc/group", "r")) == NULL) rtems_set_errno_and_return_minus_one( EINVAL ); for(;;) { if (!_libcsupport_scangr(fp, grp, buffer, bufsize)) goto error_einval; if (name) { match = (strcmp(grp->gr_name, name) == 0); } else { match = (grp->gr_gid == gid); } if (match) { fclose(fp); *result = grp; return 0; } } error_einval: fclose(fp); rtems_set_errno_and_return_minus_one( EINVAL ); } int getgrnam_r( const char *name, struct group *grp, char *buffer, size_t bufsize, struct group **result ) { return getgr_r(name, 0, grp, buffer, bufsize, result); } int getgrgid_r( gid_t gid, struct group *grp, char *buffer, size_t bufsize, struct group **result ) { return getgr_r(NULL, gid, grp, buffer, bufsize, result); }