1 /*- 2 * Copyright (c) 1990, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * Copyright (c) 2002 Networks Associates Technology, Inc. 5 * All rights reserved. 6 * 7 * Portions of this software were developed for the FreeBSD Project by 8 * ThinkSec AS and NAI Labs, the Security Research Division of Network 9 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 10 * ("CBOSS"), as part of the DARPA CHATS research program. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * @(#)edit.c 8.3 (Berkeley) 4/2/94 37 * $FreeBSD: src/usr.bin/chpass/edit.c,v 1.23 2003/04/09 18:18:42 des Exp $ 38 * $DragonFly: src/usr.bin/chpass/edit.c,v 1.3 2003/10/02 17:42:26 hmp Exp $ 39 */ 40 41 #include <sys/param.h> 42 #include <sys/stat.h> 43 44 #include <ctype.h> 45 #include <err.h> 46 #include <errno.h> 47 #include <paths.h> 48 #include <pwd.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 54 #include <pw_scan.h> 55 #include <libutil.h> 56 57 #include "chpass.h" 58 59 static int display(const char *tfn, struct passwd *pw); 60 static struct passwd *verify(const char *tfn, struct passwd *pw); 61 62 struct passwd * 63 edit(const char *tfn, struct passwd *pw) 64 { 65 struct passwd *npw; 66 char *line; 67 size_t len; 68 69 if (display(tfn, pw) == -1) 70 return (NULL); 71 for (;;) { 72 switch (pw_edit(1)) { 73 case -1: 74 return (NULL); 75 case 0: 76 return (pw_dup(pw)); 77 default: 78 break; 79 } 80 if ((npw = verify(tfn, pw)) != NULL) 81 return (npw); 82 free(npw); 83 printf("re-edit the password file? "); 84 fflush(stdout); 85 if ((line = fgetln(stdin, &len)) == NULL) { 86 warn("fgetln()"); 87 return (NULL); 88 } 89 if (len > 0 && (*line == 'N' || *line == 'n')) 90 return (NULL); 91 } 92 } 93 94 /* 95 * display -- 96 * print out the file for the user to edit; strange side-effect: 97 * set conditional flag if the user gets to edit the shell. 98 */ 99 static int 100 display(const char *tfn, struct passwd *pw) 101 { 102 FILE *fp; 103 char *bp, *gecos, *p; 104 105 if ((fp = fopen(tfn, "w")) == NULL) { 106 warn("%s", tfn); 107 return (-1); 108 } 109 110 fprintf(fp, "#Changing user information for %s.\n", pw->pw_name); 111 if (master_mode) { 112 fprintf(fp, "Login: %s\n", pw->pw_name); 113 fprintf(fp, "Password: %s\n", pw->pw_passwd); 114 fprintf(fp, "Uid [#]: %lu\n", (unsigned long)pw->pw_uid); 115 fprintf(fp, "Gid [# or name]: %lu\n", 116 (unsigned long)pw->pw_gid); 117 fprintf(fp, "Change [month day year]: %s\n", 118 ttoa(pw->pw_change)); 119 fprintf(fp, "Expire [month day year]: %s\n", 120 ttoa(pw->pw_expire)); 121 fprintf(fp, "Class: %s\n", pw->pw_class); 122 fprintf(fp, "Home directory: %s\n", pw->pw_dir); 123 fprintf(fp, "Shell: %s\n", 124 *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL); 125 } 126 /* Only admin can change "restricted" shells. */ 127 #if 0 128 else if (ok_shell(pw->pw_shell)) 129 /* 130 * Make shell a restricted field. Ugly with a 131 * necklace, but there's not much else to do. 132 */ 133 #else 134 else if ((!list[E_SHELL].restricted && ok_shell(pw->pw_shell)) || 135 master_mode) 136 /* 137 * If change not restrict (table.c) and standard shell 138 * OR if root, then allow editing of shell. 139 */ 140 #endif 141 fprintf(fp, "Shell: %s\n", 142 *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL); 143 else 144 list[E_SHELL].restricted = 1; 145 146 if ((bp = gecos = strdup(pw->pw_gecos)) == NULL) { 147 warn(NULL); 148 fclose(fp); 149 return (-1); 150 } 151 152 p = strsep(&bp, ","); 153 p = strdup(p ? p : ""); 154 list[E_NAME].save = p; 155 if (!list[E_NAME].restricted || master_mode) 156 fprintf(fp, "Full Name: %s\n", p); 157 158 p = strsep(&bp, ","); 159 p = strdup(p ? p : ""); 160 list[E_LOCATE].save = p; 161 if (!list[E_LOCATE].restricted || master_mode) 162 fprintf(fp, "Office Location: %s\n", p); 163 164 p = strsep(&bp, ","); 165 p = strdup(p ? p : ""); 166 list[E_BPHONE].save = p; 167 if (!list[E_BPHONE].restricted || master_mode) 168 fprintf(fp, "Office Phone: %s\n", p); 169 170 p = strsep(&bp, ","); 171 p = strdup(p ? p : ""); 172 list[E_HPHONE].save = p; 173 if (!list[E_HPHONE].restricted || master_mode) 174 fprintf(fp, "Home Phone: %s\n", p); 175 176 bp = strdup(bp ? bp : ""); 177 list[E_OTHER].save = bp; 178 if (!list[E_OTHER].restricted || master_mode) 179 fprintf(fp, "Other information: %s\n", bp); 180 181 free(gecos); 182 183 fchown(fileno(fp), getuid(), getgid()); 184 fclose(fp); 185 return (0); 186 } 187 188 static struct passwd * 189 verify(const char *tfn, struct passwd *pw) 190 { 191 struct passwd *npw; 192 ENTRY *ep; 193 char *buf, *p, *val; 194 struct stat sb; 195 FILE *fp; 196 int line; 197 size_t len; 198 199 if ((pw = pw_dup(pw)) == NULL) 200 return (NULL); 201 if ((fp = fopen(tfn, "r")) == NULL || 202 fstat(fileno(fp), &sb) == -1) { 203 warn("%s", tfn); 204 free(pw); 205 return (NULL); 206 } 207 if (sb.st_size == 0) { 208 warnx("corrupted temporary file"); 209 fclose(fp); 210 free(pw); 211 return (NULL); 212 } 213 val = NULL; 214 for (line = 1; (buf = fgetln(fp, &len)) != NULL; ++line) { 215 if (*buf == '\0' || *buf == '#') 216 continue; 217 while (len > 0 && isspace(buf[len - 1])) 218 --len; 219 for (ep = list;; ++ep) { 220 if (!ep->prompt) { 221 warnx("%s: unrecognized field on line %d", 222 tfn, line); 223 goto bad; 224 } 225 if (ep->len > len) 226 continue; 227 if (strncasecmp(buf, ep->prompt, ep->len) != 0) 228 continue; 229 if (ep->restricted && !master_mode) { 230 warnx("%s: you may not change the %s field", 231 tfn, ep->prompt); 232 goto bad; 233 } 234 for (p = buf; p < buf + len && *p != ':'; ++p) 235 /* nothing */ ; 236 if (*p != ':') { 237 warnx("%s: line %d corrupted", tfn, line); 238 goto bad; 239 } 240 while (++p < buf + len && isspace(*p)) 241 /* nothing */ ; 242 free(val); 243 asprintf(&val, "%.*s", (int)(buf + len - p), p); 244 if (val == NULL) 245 goto bad; 246 if (ep->except && strpbrk(val, ep->except)) { 247 warnx("%s: invalid character in \"%s\" field '%s'", 248 tfn, ep->prompt, val); 249 goto bad; 250 } 251 if ((ep->func)(val, pw, ep)) 252 goto bad; 253 break; 254 } 255 } 256 free(val); 257 fclose(fp); 258 259 /* Build the gecos field. */ 260 len = asprintf(&p, "%s,%s,%s,%s,%s", list[E_NAME].save, 261 list[E_LOCATE].save, list[E_BPHONE].save, 262 list[E_HPHONE].save, list[E_OTHER].save); 263 if (p == NULL) { 264 warn("asprintf()"); 265 free(pw); 266 return (NULL); 267 } 268 while (len > 0 && p[len - 1] == ',') 269 p[--len] = '\0'; 270 pw->pw_gecos = p; 271 buf = pw_make(pw); 272 free(pw); 273 free(p); 274 if (buf == NULL) { 275 warn("pw_make()"); 276 return (NULL); 277 } 278 npw = pw_scan(buf, PWSCAN_WARN|PWSCAN_MASTER); 279 free(buf); 280 return (npw); 281 bad: 282 free(pw); 283 free(val); 284 fclose(fp); 285 return (NULL); 286 } 287