1/* 2 * Copyright (c) 1999-2010 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23#include <ctype.h> 24#include <err.h> 25#include <errno.h> 26#include <fcntl.h> 27#include <pwd.h> 28#include <signal.h> 29#include <stdbool.h> 30#include <stdlib.h> 31#include <stdio.h> 32#include <string.h> 33#include <sysexits.h> 34#include <unistd.h> 35#include <sys/time.h> 36#include <sys/stat.h> 37#include "passwd.h" 38 39#define _PASSWD_FILE "/etc/master.passwd" 40#define _COMPAT_FILE "/etc/passwd" 41#define _PASSWD_FIELDS 10 42#define BUFSIZE 8192 43 44void getpasswd(char *, int, int, int, int, char *, char **, char**, char **); 45 46static struct passwd * 47parse_user(char *line, size_t len) 48{ 49 static struct passwd pw; 50 int i,j; 51 char *tokens[_PASSWD_FIELDS]; 52 char *token = NULL; 53 bool comment = true; 54 55 free(pw.pw_name); 56 free(pw.pw_passwd); 57 free(pw.pw_class); 58 free(pw.pw_gecos); 59 free(pw.pw_dir); 60 free(pw.pw_shell); 61 memset(&pw, 0, sizeof(pw)); 62 63 if (line == NULL) return NULL; 64 65 memset(&tokens, 0, sizeof(char *) * _PASSWD_FIELDS); 66 67 for (i = 0, j = 0; i < len && j < _PASSWD_FIELDS; ++i) { 68 int c = line[i]; 69 if (!isspace(c) && c != '#') { 70 comment = false; 71 } 72 if (!comment && token == NULL) { 73 // start a new token 74 token = &line[i]; 75 } else if (token && (c == ':' || c == '\n')) { 76 // end the current token 77 // special case for empty token 78 while (token[0] == ':' && token < &line[i]) { 79 tokens[j++] = strdup(""); 80 ++token; 81 } 82 tokens[j++] = strndup(token, &line[i] - token); 83 token = NULL; 84 } 85 } 86 87 if (comment || j != _PASSWD_FIELDS) return NULL; 88 89 j = 0; 90 pw.pw_name = tokens[j++]; 91 pw.pw_passwd = tokens[j++]; 92 pw.pw_uid = atoi(tokens[j]); 93 free(tokens[j++]); 94 pw.pw_gid = atoi(tokens[j]); 95 free(tokens[j++]); 96 pw.pw_class = tokens[j++]; 97 pw.pw_change = atoi(tokens[j]); 98 free(tokens[j++]); 99 pw.pw_expire = atoi(tokens[j]); 100 free(tokens[j++]); 101 pw.pw_gecos = tokens[j++]; 102 pw.pw_dir = tokens[j++]; 103 pw.pw_shell = tokens[j++]; 104 105 return &pw; 106} 107 108static struct passwd * 109find_user(FILE *fp, char *uname) 110{ 111 size_t len; 112 char *line; 113 114 rewind(fp); 115 116 while ((line = fgetln(fp, &len)) != NULL) { 117 struct passwd *pw = parse_user(line, len); 118 if (pw && strcmp(uname, pw->pw_name) == 0) { 119 return pw; 120 } 121 } 122 return NULL; 123} 124 125static void 126rewrite_file(char *path, FILE *fp, struct passwd *newpw) 127{ 128 int fd; 129 char *line; 130 size_t len; 131 FILE *tfp = NULL; 132 char *tempname = NULL; // temporary master.passwd file 133 134 asprintf(&tempname, "%s.XXXXXX", path); 135 136 fd = mkstemp(tempname); 137 if (fd == -1) { 138 err(EXIT_FAILURE, "%s", tempname); 139 } 140 tfp = fdopen(fd, "w+"); 141 if (tfp == NULL || fchmod(fd, S_IRUSR | S_IWUSR) != 0) { 142 int save = errno; 143 unlink(tempname); 144 errno = save; 145 err(EXIT_FAILURE, "%s", tempname); 146 } 147 148 while ((line = fgetln(fp, &len)) != NULL) { 149 struct passwd *pw = parse_user(line, len); 150 151 // if this is not the entry we're looking for or if parsing 152 // failed (likely a comment) then print the entry as is. 153 if (pw == NULL || strcmp(newpw->pw_name, pw->pw_name) != 0) { 154 fwrite(line, sizeof(char), len, tfp); 155 } else { 156 fprintf(tfp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", 157 newpw->pw_name, 158 newpw->pw_passwd, 159 newpw->pw_uid, 160 newpw->pw_gid, 161 newpw->pw_class, 162 newpw->pw_change, 163 newpw->pw_expire, 164 newpw->pw_gecos, 165 newpw->pw_dir, 166 newpw->pw_shell); 167 } 168 } 169 170 // Move the temporary file into place. 171 if (fclose(tfp) != 0 || rename(tempname, path) != 0) { 172 int save = errno; 173 unlink(tempname); 174 errno = save; 175 err(EXIT_FAILURE, "%s", tempname); 176 } 177 178 free(tempname); 179} 180 181int 182file_passwd(char *uname, char *locn) 183{ 184 char *ne, *oc, *nc; 185 int fd; 186 FILE *fp; 187 uid_t uid; 188 char *fname; 189 struct passwd *pw; 190 struct passwd newpw; 191 192 fname = _PASSWD_FILE; 193 if (locn != NULL) fname = locn; 194 195 fd = open(fname, O_RDONLY | O_EXLOCK); 196 if (fd == -1) { 197 err(EXIT_FAILURE, "%s", fname); 198 } 199 200 fp = fdopen(fd, "r"); 201 if (fp == NULL) { 202 err(EXIT_FAILURE, "%s", fname); 203 } 204 205 pw = find_user(fp, uname); 206 if (pw == NULL) { 207 errx(EXIT_FAILURE, "user %s not found in %s", uname, fname); 208 } 209 210 uid = getuid(); 211 if (uid != 0 && uid != pw->pw_uid) { 212 errno = EACCES; 213 err(EXIT_FAILURE, "%s", uname); 214 } 215 216 // Get the password 217 getpasswd(uname, (uid == 0), 5, 0, 0, pw->pw_passwd, &ne, &oc, &nc); 218 219 newpw.pw_name = strdup(pw->pw_name); 220 newpw.pw_passwd = strdup(ne); 221 newpw.pw_uid = pw->pw_uid; 222 newpw.pw_gid = pw->pw_gid; 223 newpw.pw_class = strdup(pw->pw_class); 224 newpw.pw_change = pw->pw_change; 225 newpw.pw_expire = pw->pw_expire; 226 newpw.pw_gecos = strdup(pw->pw_gecos); 227 newpw.pw_dir = strdup(pw->pw_dir); 228 newpw.pw_shell = strdup(pw->pw_shell); 229 230 // Rewrite the file 231 rewind(fp); 232 rewrite_file(fname, fp, &newpw); 233 234 fclose(fp); 235 236 return 0; 237} 238