/*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1999-2010 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
- * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
- * Reserved. This file contains Original Code and/or Modifications of
- * Original Code as defined in and that are subject to the Apple Public
- * Source License Version 1.0 (the 'License'). You may not use this file
- * except in compliance with the License. Please obtain a copy of the
- * License at http://www.apple.com/publicsource and read it before using
- * this file.
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
- * License for the specific language governing rights and limitations
- * under the License."
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#include <stdlib.h>
+#include <sysexits.h>
#include <unistd.h>
-#include <pwd.h>
-#include <errno.h>
-#include "stringops.h"
-
-#define TEMP_FILE "/tmp/.pwtmp"
+#include <sys/time.h>
+#include <sys/stat.h>
+#include "passwd.h"
#define _PASSWD_FILE "/etc/master.passwd"
#define _COMPAT_FILE "/etc/passwd"
#define _PASSWD_FIELDS 10
#define BUFSIZE 8192
-extern void getpasswd(char *, int, int, int, int, char *, char **, char**, char **);
-
-static int do_compat = 1;
-
-char *
-getline(FILE *fp)
-{
- static char s[BUFSIZE];
- int len;
-
- s[0] = '\0';
-
- fgets(s, BUFSIZE, fp);
- if (s == NULL || s[0] == '\0') return NULL;
-
- if (s[0] == '#') return s;
-
- len = strlen(s) - 1;
- s[len] = '\0';
+void getpasswd(char *, int, int, int, int, char *, char **, char**, char **);
- return s;
-}
-
-struct passwd *
-parse_user(char *line)
+static struct passwd *
+parse_user(char *line, size_t len)
{
- static struct passwd pw = {0};
- char **tokens;
- int i, len;
-
- if (pw.pw_name != NULL) free(pw.pw_name);
- pw.pw_name = NULL;
- if (pw.pw_passwd != NULL) free(pw.pw_passwd);
- pw.pw_passwd = NULL;
- if (pw.pw_gecos != NULL) free(pw.pw_gecos);
- pw.pw_gecos = NULL;
- if (pw.pw_dir != NULL) free(pw.pw_dir);
- pw.pw_dir = NULL;
- if (pw.pw_shell != NULL) free(pw.pw_shell);
- pw.pw_shell = NULL;
-
- if (pw.pw_class != NULL) free(pw.pw_class);
- pw.pw_class = NULL;
-
- if (line == NULL) return (struct passwd *)NULL;
- tokens = explode(line, ':');
- len = listLength(tokens);
-
- if (len != _PASSWD_FIELDS)
- {
- freeList(tokens);
- return (struct passwd *)NULL;
+ static struct passwd pw;
+ int i,j;
+ char *tokens[_PASSWD_FIELDS];
+ char *token = NULL;
+ bool comment = true;
+
+ free(pw.pw_name);
+ free(pw.pw_passwd);
+ free(pw.pw_class);
+ free(pw.pw_gecos);
+ free(pw.pw_dir);
+ free(pw.pw_shell);
+ memset(&pw, 0, sizeof(pw));
+
+ if (line == NULL) return NULL;
+
+ memset(&tokens, 0, sizeof(char *) * _PASSWD_FIELDS);
+
+ for (i = 0, j = 0; i < len && j < _PASSWD_FIELDS; ++i) {
+ int c = line[i];
+ if (!isspace(c) && c != '#') {
+ comment = false;
+ }
+ if (!comment && token == NULL) {
+ // start a new token
+ token = &line[i];
+ } else if (token && (c == ':' || c == '\n')) {
+ // end the current token
+ // special case for empty token
+ while (token[0] == ':' && token < &line[i]) {
+ tokens[j++] = strdup("");
+ ++token;
+ }
+ tokens[j++] = strndup(token, &line[i] - token);
+ token = NULL;
+ }
}
- i = 0;
- pw.pw_name = tokens[i++];
- pw.pw_passwd = tokens[i++];
- pw.pw_uid = atoi(tokens[i]);
- free(tokens[i++]);
- pw.pw_gid = atoi(tokens[i]);
- free(tokens[i++]);
- pw.pw_class = tokens[i++];
- pw.pw_change = atoi(tokens[i]);
- free(tokens[i++]);
- pw.pw_expire = atoi(tokens[i]);
- free(tokens[i++]);
- pw.pw_gecos = tokens[i++];
- pw.pw_dir = tokens[i++];
- pw.pw_shell = tokens[i++];
+ if (comment || j != _PASSWD_FIELDS) return NULL;
+
+ j = 0;
+ pw.pw_name = tokens[j++];
+ pw.pw_passwd = tokens[j++];
+ pw.pw_uid = atoi(tokens[j]);
+ free(tokens[j++]);
+ pw.pw_gid = atoi(tokens[j]);
+ free(tokens[j++]);
+ pw.pw_class = tokens[j++];
+ pw.pw_change = atoi(tokens[j]);
+ free(tokens[j++]);
+ pw.pw_expire = atoi(tokens[j]);
+ free(tokens[j++]);
+ pw.pw_gecos = tokens[j++];
+ pw.pw_dir = tokens[j++];
+ pw.pw_shell = tokens[j++];
return &pw;
}
-struct passwd *
-find_user(char *uname, FILE *fp)
+static struct passwd *
+find_user(FILE *fp, char *uname)
{
+ size_t len;
char *line;
- struct passwd *pw;
rewind(fp);
- while (NULL != (line = getline(fp)))
- {
- if (line[0] == '#') continue;
- pw = parse_user(line);
- if (pw == (struct passwd *)NULL) continue;
- if (!strcmp(uname, pw->pw_name)) return pw;
+ while ((line = fgetln(fp, &len)) != NULL) {
+ struct passwd *pw = parse_user(line, len);
+ if (pw && strcmp(uname, pw->pw_name) == 0) {
+ return pw;
+ }
}
-
- pw = parse_user(NULL);
- return (struct passwd *)NULL;
+ return NULL;
}
-void
-rewrite_file(char *pwname, FILE *fp, struct passwd *newpw)
+static void
+rewrite_file(char *path, FILE *fp, struct passwd *newpw)
{
+ int fd;
char *line;
- struct passwd *pw;
- FILE *tfp, *cfp;
- char fname[256];
-
- sprintf(fname, "%s.%d", TEMP_FILE, getpid());
+ size_t len;
+ FILE *tfp = NULL;
+ char *tempname = NULL; // temporary master.passwd file
- tfp = fopen(fname, "w+");
- if (tfp == NULL)
- {
- fprintf(stderr, "can't write temporary file \"%s\": ", fname);
- perror("");
- exit(1);
- }
-
- cfp = NULL;
- if (!strcmp(pwname, _PASSWD_FILE))
- {
- cfp = fopen(_COMPAT_FILE, "w");
- if (cfp == NULL)
- {
- fprintf(stderr, "warning: can't write compatability file \"%s\": ",
- _COMPAT_FILE);
- perror("");
- }
+ asprintf(&tempname, "%s.XXXXXX", path);
+
+ fd = mkstemp(tempname);
+ if (fd == -1) {
+ err(EXIT_FAILURE, "%s", tempname);
}
-
- if (cfp != NULL)
- {
- fprintf(cfp, "#\n");
- fprintf(cfp, "# 4.3BSD-compatable User Database\n");
- fprintf(cfp, "#\n");
- fprintf(cfp, "# Note that this file is not consulted for login.\n");
- fprintf(cfp, "# It only exisits for compatability with 4.3BSD utilities.\n");
- fprintf(cfp, "#\n");
- fprintf(cfp, "# This file is automatically re-written by various system utilities.\n");
- fprintf(cfp, "# Do not edit this file. Changes will be lost.\n");
- fprintf(cfp, "#\n");
+ tfp = fdopen(fd, "w+");
+ if (tfp == NULL || fchmod(fd, S_IRUSR | S_IWUSR) != 0) {
+ int save = errno;
+ unlink(tempname);
+ errno = save;
+ err(EXIT_FAILURE, "%s", tempname);
}
-
- rewind(fp);
-
- while (NULL != (line = getline(fp)))
- {
- if (line[0] == '#')
- {
- fprintf(tfp, "%s", line);
- continue;
- }
-
- pw = parse_user(line);
- if (pw == (struct passwd *)NULL)
- {
- fprintf(stderr, "warning: bad format for entry: \"%s\"\n", line);
- fprintf(tfp, "%s\n", line);
- if (cfp != NULL) fprintf(cfp, "%s\n", line);
- continue;
- }
-
- if (strcmp(newpw->pw_name, pw->pw_name))
- {
- fprintf(tfp, "%s\n", line);
- if (cfp != NULL) fprintf(cfp, "%s\n", line);
- continue;
- }
-
- fprintf(tfp, "%s:%s:%d:%d:%s:%d:%d:%s:%s:%s\n",
- newpw->pw_name, newpw->pw_passwd, newpw->pw_uid, newpw->pw_gid,
- newpw->pw_class, newpw->pw_change, newpw->pw_expire,
- newpw->pw_gecos, newpw->pw_dir, newpw->pw_shell);
- if (cfp != NULL)
- {
- fprintf(cfp, "%s:",newpw->pw_name);
- if ((newpw->pw_passwd == NULL) || (newpw->pw_passwd[0] == '\0'))
- fprintf(cfp, ":");
- else
- fprintf(cfp, "*:");
- fprintf(cfp, "%d:%d:%s:%s:%s\n",
- newpw->pw_uid, newpw->pw_gid, newpw->pw_gecos,
- newpw->pw_dir, newpw->pw_shell);
+
+ while ((line = fgetln(fp, &len)) != NULL) {
+ struct passwd *pw = parse_user(line, len);
+
+ // if this is not the entry we're looking for or if parsing
+ // failed (likely a comment) then print the entry as is.
+ if (pw == NULL || strcmp(newpw->pw_name, pw->pw_name) != 0) {
+ fwrite(line, sizeof(char), len, tfp);
+ } else {
+ fprintf(tfp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
+ newpw->pw_name,
+ newpw->pw_passwd,
+ newpw->pw_uid,
+ newpw->pw_gid,
+ newpw->pw_class,
+ newpw->pw_change,
+ newpw->pw_expire,
+ newpw->pw_gecos,
+ newpw->pw_dir,
+ newpw->pw_shell);
}
}
- if (cfp != NULL) fclose(cfp);
- fclose(fp);
- if (unlink(pwname) < 0)
- {
- fprintf(stderr, "can't update \"%s\": ", pwname);
- perror("");
- }
-
- rewind(tfp);
-
- fp = fopen(pwname, "w");
- if (fp == NULL)
- {
- fprintf(stderr, "ERROR: lost file \"%s\"\n", pwname);
- fprintf(stderr, "new passwd file is \"%s\"\n", fname);
- perror("open");
- exit(1);
+ // Move the temporary file into place.
+ if (fclose(tfp) != 0 || rename(tempname, path) != 0) {
+ int save = errno;
+ unlink(tempname);
+ errno = save;
+ err(EXIT_FAILURE, "%s", tempname);
}
- while (NULL != (line = getline(tfp)))
- {
- fprintf(fp, "%s", line);
- if (line[0] != '#') fprintf(fp, "\n");
- }
- fclose(fp);
- fclose(tfp);
- unlink(fname);
+ free(tempname);
}
int
file_passwd(char *uname, char *locn)
{
char *ne, *oc, *nc;
+ int fd;
FILE *fp;
+ uid_t uid;
char *fname;
struct passwd *pw;
struct passwd newpw;
- int uid;
-
+
fname = _PASSWD_FILE;
if (locn != NULL) fname = locn;
-
- fp = fopen(fname, "a+");
- if (fp == NULL)
- {
- fprintf(stderr, "can't write to file \"%s\": ", fname);
- perror("");
- exit(1);
+
+ fd = open(fname, O_RDONLY | O_EXLOCK);
+ if (fd == -1) {
+ err(EXIT_FAILURE, "%s", fname);
}
+ fp = fdopen(fd, "r");
+ if (fp == NULL) {
+ err(EXIT_FAILURE, "%s", fname);
+ }
- pw = find_user(uname, fp);
- if (pw == (struct passwd *)NULL)
- {
- fprintf(stderr, "user %s not found in file %s\n", uname, fname);
- exit(1);
+ pw = find_user(fp, uname);
+ if (pw == NULL) {
+ errx(EXIT_FAILURE, "user %s not found in %s", uname, fname);
}
uid = getuid();
- if ((uid != 0) && (uid != pw->pw_uid))
- {
- fprintf(stderr, "Permission denied\n");
- exit(1);
+ if (uid != 0 && uid != pw->pw_uid) {
+ errno = EACCES;
+ err(EXIT_FAILURE, "%s", uname);
}
- /*
- * Get the new password
- */
+ // Get the password
getpasswd(uname, (uid == 0), 5, 0, 0, pw->pw_passwd, &ne, &oc, &nc);
- newpw.pw_name = copyString(pw->pw_name);
- newpw.pw_passwd = copyString(ne);
+ newpw.pw_name = strdup(pw->pw_name);
+ newpw.pw_passwd = strdup(ne);
newpw.pw_uid = pw->pw_uid;
newpw.pw_gid = pw->pw_gid;
- newpw.pw_class = copyString(pw->pw_class);
+ newpw.pw_class = strdup(pw->pw_class);
newpw.pw_change = pw->pw_change;
newpw.pw_expire = pw->pw_expire;
- newpw.pw_gecos = copyString(pw->pw_gecos);
- newpw.pw_dir = copyString(pw->pw_dir);
- newpw.pw_shell = copyString(pw->pw_shell);
+ newpw.pw_gecos = strdup(pw->pw_gecos);
+ newpw.pw_dir = strdup(pw->pw_dir);
+ newpw.pw_shell = strdup(pw->pw_shell);
- /*
- * Re-write the file
- */
+ // Rewrite the file
+ rewind(fp);
rewrite_file(fname, fp, &newpw);
- /*
- * Clean up memory
- */
- pw = parse_user(NULL);
- free(newpw.pw_name);
- free(newpw.pw_passwd);
- free(newpw.pw_gecos);
- free(newpw.pw_dir);
- free(newpw.pw_shell);
- free(newpw.pw_class);
-
fclose(fp);
return 0;