]> git.saurik.com Git - apple/system_cmds.git/blob - passwd.tproj/file_passwd.c
a522730dc36928a8e636a4fc850256a661d39713
[apple/system_cmds.git] / passwd.tproj / file_passwd.c
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
44 void getpasswd(char *, int, int, int, int, char *, char **, char**, char **);
45
46 static struct passwd *
47 parse_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
108 static struct passwd *
109 find_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
125 static void
126 rewrite_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
181 int
182 file_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 }