static struct group * vnextgrent(char const * nam, gid_t gid, int doclose) { struct group * gr = NULL; static char * grtmp = NULL; static int grlen = 0; static char ** mems = NULL; static int memlen = 0; extendline(&grtmp, &grlen, MAXPATHLEN); strlcpy(grtmp, getgrpath(_GROUP), MAXPATHLEN); if (grp_fp != NULL || (grp_fp = fopen(grtmp, "r")) != NULL) { int done = 0; static struct group grp; while (!done && fgets(grtmp, grlen, grp_fp) != NULL) { int i, quickout = 0; int mno = 0; char * q, * p; char * sep = ":\n"; if ((p = strchr(grtmp, '\n')) == NULL) { int l; extendline(&grtmp, &grlen, grlen + PWBUFSZ); l = strlen(grtmp); if (fgets(grtmp + l, grlen - l, grp_fp) == NULL) break; /* No newline terminator on last line */ } /* Skip comments and empty lines */ if (*grtmp == '\n' || *grtmp == '#') continue; i = 0; q = p = grtmp; bzero(&grp, sizeof grp); extendarray(&mems, &memlen, 200); while (!quickout && (p = strsep(&q, sep)) != NULL) { switch (i++) { case 0: /* groupname */ grp.gr_name = p; if (nam) { if (strcmp(nam, p) == 0) done = 1; else quickout = 1; } break; case 1: /* password */ grp.gr_passwd = p; break; case 2: /* gid */ grp.gr_gid = atoi(p); if (gid != (gid_t)-1) { if (gid == (gid_t)grp.gr_gid) done = 1; else quickout = 1; } else if (nam == NULL) done = 1; break; case 3: q = p; sep = ",\n"; break; default: if (*p) { extendarray(&mems, &memlen, mno + 2); mems[mno++] = p; } break; } } grp.gr_mem = mems; mems[mno] = NULL; } if (doclose) vendgrent(); if (done && grp.gr_name) { gr = &grp; CKNULL(grp.gr_passwd); } } return gr; }
int write_userconfig(char const * file) { int fd; if (file == NULL) file = _PATH_PW_CONF; if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) { FILE *fp; if ((fp = fdopen(fd, "w")) == NULL) close(fd); else { int i, j, k; int len = LNBUFSZ; char *buf = malloc(len); for (i = _UC_NONE; i < _UC_FIELDS; i++) { int quote = 1; char const *val = buf; *buf = '\0'; switch (i) { case _UC_DEFAULTPWD: val = boolean_str(config.default_password); break; case _UC_REUSEUID: val = boolean_str(config.reuse_uids); break; case _UC_REUSEGID: val = boolean_str(config.reuse_gids); break; case _UC_NISPASSWD: val = config.nispasswd ? config.nispasswd : ""; quote = 0; break; case _UC_DOTDIR: val = config.dotdir ? config.dotdir : boolean_str(0); break; case _UC_NEWMAIL: val = config.newmail ? config.newmail : boolean_str(0); break; case _UC_LOGFILE: val = config.logfile ? config.logfile : boolean_str(0); break; case _UC_HOMEROOT: val = config.home; break; case _UC_HOMEMODE: sprintf(buf, "%04o", config.homemode); quote = 0; break; case _UC_SHELLPATH: val = config.shelldir; break; case _UC_SHELLS: for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) { char lbuf[64]; int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", system_shells[j]); if (l < 0) l = 0; if (l + k + 1 < len || extendline(&buf, &len, len + LNBUFSZ) != -1) { strcpy(buf + k, lbuf); k += l; } } quote = 0; break; case _UC_DEFAULTSHELL: val = config.shell_default ? config.shell_default : bourne_shell; break; case _UC_DEFAULTGROUP: val = config.default_group ? config.default_group : ""; break; case _UC_EXTRAGROUPS: extendarray(&config.groups, &config.numgroups, 200); for (j = k = 0; j < config.numgroups && config.groups[j] != NULL; j++) { char lbuf[64]; int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", config.groups[j]); if (l < 0) l = 0; if (l + k + 1 < len || extendline(&buf, &len, len + 1024) != -1) { strcpy(buf + k, lbuf); k += l; } } quote = 0; break; case _UC_DEFAULTCLASS: val = config.default_class ? config.default_class : ""; break; case _UC_MINUID: sprintf(buf, "%lu", (unsigned long) config.min_uid); quote = 0; break; case _UC_MAXUID: sprintf(buf, "%lu", (unsigned long) config.max_uid); quote = 0; break; case _UC_MINGID: sprintf(buf, "%lu", (unsigned long) config.min_gid); quote = 0; break; case _UC_MAXGID: sprintf(buf, "%lu", (unsigned long) config.max_gid); quote = 0; break; case _UC_EXPIRE: sprintf(buf, "%d", config.expire_days); quote = 0; break; case _UC_PASSWORD: sprintf(buf, "%d", config.password_days); quote = 0; break; case _UC_NONE: break; } if (comments[i]) fputs(comments[i], fp); if (*kwds[i]) { if (quote) fprintf(fp, "%s = \"%s\"\n", kwds[i], val); else fprintf(fp, "%s = %s\n", kwds[i], val); #if debugging printf("WROTE: %s = %s\n", kwds[i], val); #endif } } free(buf); return fclose(fp) != EOF; } } return 0; }
struct userconf * read_userconfig(char const * file) { FILE *fp; extendarray(&config.groups, &config.numgroups, 200); memset(config.groups, 0, config.numgroups * sizeof(char *)); if (file == NULL) file = _PATH_PW_CONF; if ((fp = fopen(file, "r")) != NULL) { int buflen = LNBUFSZ; char *buf = malloc(buflen); nextline: while (fgets(buf, buflen, fp) != NULL) { char *p; while ((p = strchr(buf, '\n')) == NULL) { int l; if (extendline(&buf, &buflen, buflen + LNBUFSZ) == -1) { int ch; while ((ch = fgetc(fp)) != '\n' && ch != EOF); goto nextline; /* Ignore it */ } l = strlen(buf); if (fgets(buf + l, buflen - l, fp) == NULL) break; /* Unterminated last line */ } if (p != NULL) *p = '\0'; if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') { static char const toks[] = " \t\r\n,="; char *q = strtok(NULL, toks); int i = 0; while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0) ++i; #if debugging if (i == _UC_FIELDS) printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : ""); else printf("Got kwd[%s]=%s\n", p, q); #endif switch (i) { case _UC_DEFAULTPWD: config.default_password = boolean_val(q, 1); break; case _UC_REUSEUID: config.reuse_uids = boolean_val(q, 0); break; case _UC_REUSEGID: config.reuse_gids = boolean_val(q, 0); break; case _UC_NISPASSWD: config.nispasswd = (q == NULL || !boolean_val(q, 1)) ? NULL : newstr(q); break; case _UC_DOTDIR: config.dotdir = (q == NULL || !boolean_val(q, 1)) ? NULL : newstr(q); break; case _UC_NEWMAIL: config.newmail = (q == NULL || !boolean_val(q, 1)) ? NULL : newstr(q); break; case _UC_LOGFILE: config.logfile = (q == NULL || !boolean_val(q, 1)) ? NULL : newstr(q); break; case _UC_HOMEROOT: config.home = (q == NULL || !boolean_val(q, 1)) ? "/home" : newstr(q); break; case _UC_SHELLPATH: config.shelldir = (q == NULL || !boolean_val(q, 1)) ? "/bin" : newstr(q); break; case _UC_SHELLS: for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks)) system_shells[i] = newstr(q); if (i > 0) while (i < _UC_MAXSHELLS) system_shells[i++] = NULL; break; case _UC_DEFAULTSHELL: config.shell_default = (q == NULL || !boolean_val(q, 1)) ? (char *) bourne_shell : newstr(q); break; case _UC_DEFAULTGROUP: q = unquote(q); config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL) ? NULL : newstr(q); break; case _UC_EXTRAGROUPS: for (i = 0; q != NULL; q = strtok(NULL, toks)) { if (extendarray(&config.groups, &config.numgroups, i + 2) != -1) config.groups[i++] = newstr(q); } if (i > 0) while (i < config.numgroups) config.groups[i++] = NULL; break; case _UC_DEFAULTCLASS: config.default_class = (q == NULL || !boolean_val(q, 1)) ? NULL : newstr(q); break; case _UC_MINUID: if ((q = unquote(q)) != NULL && isdigit(*q)) config.min_uid = (uid_t) atol(q); break; case _UC_MAXUID: if ((q = unquote(q)) != NULL && isdigit(*q)) config.max_uid = (uid_t) atol(q); break; case _UC_MINGID: if ((q = unquote(q)) != NULL && isdigit(*q)) config.min_gid = (gid_t) atol(q); break; case _UC_MAXGID: if ((q = unquote(q)) != NULL && isdigit(*q)) config.max_gid = (gid_t) atol(q); break; case _UC_EXPIRE: if ((q = unquote(q)) != NULL && isdigit(*q)) config.expire_days = atoi(q); break; case _UC_PASSWORD: if ((q = unquote(q)) != NULL && isdigit(*q)) config.password_days = atoi(q); break; case _UC_FIELDS: case _UC_NONE: break; } } } free(buf); fclose(fp); } return &config; }
int editgroups(char *name, char **groups) { int rc = 0; int infd; char groupfile[MAXPATHLEN]; char grouptmp[MAXPATHLEN]; strncpy(groupfile, getgrpath(_GROUP), MAXPATHLEN - 5); groupfile[MAXPATHLEN - 5] = '\0'; strcpy(grouptmp, groupfile); strcat(grouptmp, ".new"); if ((infd = open(groupfile, O_RDWR | O_CREAT | O_EXLOCK, 0644)) != -1) { FILE *infp; if ((infp = fdopen(infd, "r+")) == NULL) close(infd); else { int outfd; if ((outfd = open(grouptmp, O_RDWR | O_CREAT | O_TRUNC, 0644)) != -1) { FILE *outfp; if ((outfp = fdopen(outfd, "w+")) == NULL) close(outfd); else { int linelen = PWBUFSZ; int outlen = PWBUFSZ; int memlen = 200; /* Arbitrary */ char *line = malloc(linelen); char *outl = malloc(outlen); char **mems = malloc(memlen * sizeof(char *)); int namlen = strlen(name); if (line == NULL || outl == NULL || mems == NULL) { mem_abort: rc = 0; } else { while (fgets(line, linelen, infp) != NULL) { char *p; int l; while ((p = strchr(line, '\n')) == NULL) { if (extendline(&line, &linelen, linelen + PWBUFSZ) == -1) { goto mem_abort; } l = strlen(line); if (fgets(line + l, linelen - l, infp) == NULL) break; /* No newline terminator on last line */ } l = strlen(line) + namlen + 1; if (extendline(&outl, &outlen, l) == -1) { goto mem_abort; } if (*line == '#') strcpy(outl, line); else if (*line == '\n') *outl = '\0'; else { int i, mno = 0; char *cp = line; char const *sep = ":\n"; struct group grp; memset(&grp, 0, sizeof grp); for (i = 0; (p = strsep(&cp, sep)) != NULL; i++) { switch (i) { case 0: /* Group name */ grp.gr_name = p; break; case 1: /* Group password */ grp.gr_passwd = p; break; case 2: /* Group id */ grp.gr_gid = atoi(p); break; case 3: /* Member list */ cp = p; sep = ",\n"; break; default: /* Individual members */ if (*p) { if (extendarray(&mems, &memlen, mno + 2) == -1) { goto mem_abort; } mems[mno++] = p; } break; } } if (i < 2) /* Bail out - insufficient fields */ continue; grp.gr_mem = mems; for (i = mno; i < memlen; i++) mems[i] = NULL; /* * Delete from group, or add to group? */ if (groups == NULL || isingroup(grp.gr_name, groups) == -1) { /* Delete */ int idx; while ((idx = isingroup(name, mems)) != -1) { for (i = idx; i < (memlen - 1); i++) mems[i] = mems[i + 1]; mems[i] = NULL; --mno; } /* * Special case - deleting user and group may be user's own */ if (groups == NULL && mems[0] == NULL && strcmp(name, grp.gr_name) == 0) { /* * First, make _sure_ we don't have other members */ struct passwd *pwd; SETPWENT(); while ((pwd = GETPWENT()) != NULL && (gid_t)pwd->pw_gid != (gid_t)grp.gr_gid); ENDPWENT(); if (pwd == NULL) /* No members at all */ continue; /* Drop the group */ } } else if (isingroup(name, mems) == -1) { if (extendarray(&mems, &memlen, mno + 2) == -1) { goto mem_abort; } grp.gr_mem = mems; /* May have realloced() */ mems[mno++] = name; mems[mno ] = NULL; } fmtgrentry(&outl, &outlen, &grp, PWF_GROUP); } fputs(outl, outfp); } if (fflush(outfp) != EOF) { rc = 1; /* * Copy data back into the original file and truncate */ rewind(infp); rewind(outfp); while (fgets(outl, outlen, outfp) != NULL) fputs(outl, infp); /* * This is a gross hack, but we may have corrupted the * original file. */ if (fflush(infp) == EOF || ferror(infp)) rc = rename(grouptmp, groupfile) == 0; else ftruncate(infd, ftell(infp)); } } free(mems); free(outl); free(line); fclose(outfp); } remove(grouptmp); } fclose(infp); } } return rc; }
int fileupdate(char const * filename, mode_t fmode, char const * newline, char const * prefix, int pfxlen, int updmode) { int rc = 0; if (pfxlen <= 1) rc = EINVAL; else { int infd = open(filename, O_RDWR | O_CREAT, fmode); if (infd == -1) rc = errno; else { FILE *infp = fdopen(infd, "r+"); if (infp == NULL) { rc = errno; /* Assumes fopen(3) sets errno from open(2) */ close(infd); } else { int outfd; char file[MAXPATHLEN]; strcpy(file, filename); strcat(file, ".new"); outfd = open(file, O_RDWR | O_CREAT | O_TRUNC | O_EXLOCK, fmode); if (outfd == -1) rc = errno; else { FILE *outfp = fdopen(outfd, "w+"); if (outfp == NULL) { rc = errno; close(outfd); } else { int updated = UPD_CREATE; int linesize = PWBUFSZ; char *line = malloc(linesize); nextline: while (fgets(line, linesize, infp) != NULL) { char *p = strchr(line, '\n'); while ((p = strchr(line, '\n')) == NULL) { int l; if (extendline(&line, &linesize, linesize + PWBUFSZ) == -1) { int ch; fputs(line, outfp); while ((ch = fgetc(infp)) != EOF) { fputc(ch, outfp); if (ch == '\n') break; } goto nextline; } l = strlen(line); if (fgets(line + l, linesize - l, infp) == NULL) break; } if (*line != '#' && *line != '\n') { if (!updated && strncmp(line, prefix, pfxlen) == 0) { updated = updmode == UPD_REPLACE ? UPD_REPLACE : UPD_DELETE; /* * Only actually write changes if updating */ if (updmode == UPD_REPLACE) strcpy(line, newline); else if (updmode == UPD_DELETE) continue; } } fputs(line, outfp); } /* * Now, we need to decide what to do: If we are in * update mode, and no record was updated, then error If * we are in insert mode, and record already exists, * then error */ if (updmode != updated) /* -1 return means: * update,delete=no user entry * create=entry exists */ rc = -1; else { /* * If adding a new record, append it to the end */ if (updmode == UPD_CREATE) fputs(newline, outfp); /* * Flush the file and check for the result */ if (fflush(outfp) == EOF) rc = errno; /* Failed to update */ else { /* * Copy data back into the * original file and truncate */ rewind(infp); rewind(outfp); while (fgets(line, linesize, outfp) != NULL) fputs(line, infp); /* * If there was a problem with copying * we will just rename 'file.new' * to 'file'. * This is a gross hack, but we may have * corrupted the original file * Unfortunately, it will lose the inode * and hence the lock. */ if (fflush(infp) == EOF || ferror(infp)) rename(file, filename); else ftruncate(infd, ftell(infp)); } } free(line); fclose(outfp); } remove(file); } fclose(infp); } } } return rc; }