1 /*- 2 * Copyright (c) 1990, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * $FreeBSD$ 34 */ 35 36 #ifndef lint 37 static const char sccsid[] = "@(#)edit.c 8.3 (Berkeley) 4/2/94"; 38 #endif /* not lint */ 39 40 #include <sys/param.h> 41 #include <sys/stat.h> 42 43 #include <ctype.h> 44 #include <err.h> 45 #include <errno.h> 46 #include <md5.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 <pw_util.h> 56 57 #include "chpass.h" 58 #ifdef YP 59 #include "pw_yp.h" 60 #endif /* YP */ 61 62 extern char *tempname; 63 64 void 65 edit(pw) 66 struct passwd *pw; 67 { 68 struct stat begin, end; 69 char *begin_sum, *end_sum; 70 71 for (;;) { 72 if (stat(tempname, &begin)) 73 pw_error(tempname, 1, 1); 74 begin_sum = MD5File(tempname, (char *)NULL); 75 pw_edit(1); 76 if (stat(tempname, &end)) 77 pw_error(tempname, 1, 1); 78 end_sum = MD5File(tempname, (char *)NULL); 79 if ((begin.st_mtime == end.st_mtime) && 80 (strcmp(begin_sum, end_sum) == 0)) { 81 warnx("no changes made"); 82 pw_error(NULL, 0, 0); 83 } 84 free(begin_sum); 85 free(end_sum); 86 if (verify(pw)) 87 break; 88 pw_prompt(); 89 } 90 } 91 92 /* 93 * display -- 94 * print out the file for the user to edit; strange side-effect: 95 * set conditional flag if the user gets to edit the shell. 96 */ 97 void 98 display(fd, pw) 99 int fd; 100 struct passwd *pw; 101 { 102 FILE *fp; 103 char *bp, *p, *ttoa(); 104 105 if (!(fp = fdopen(fd, "w"))) 106 pw_error(tempname, 1, 1); 107 108 (void)fprintf(fp, 109 #ifdef YP 110 "#Changing %s information for %s.\n", _use_yp ? "NIS" : "user database", pw->pw_name); 111 if (!uid && (!_use_yp || suser_override)) { 112 #else 113 "#Changing user database information for %s.\n", pw->pw_name); 114 if (!uid) { 115 #endif /* YP */ 116 (void)fprintf(fp, "Login: %s\n", pw->pw_name); 117 (void)fprintf(fp, "Password: %s\n", pw->pw_passwd); 118 (void)fprintf(fp, "Uid [#]: %d\n", pw->pw_uid); 119 (void)fprintf(fp, "Gid [# or name]: %d\n", pw->pw_gid); 120 (void)fprintf(fp, "Change [month day year]: %s\n", 121 ttoa(pw->pw_change)); 122 (void)fprintf(fp, "Expire [month day year]: %s\n", 123 ttoa(pw->pw_expire)); 124 (void)fprintf(fp, "Class: %s\n", pw->pw_class); 125 (void)fprintf(fp, "Home directory: %s\n", pw->pw_dir); 126 (void)fprintf(fp, "Shell: %s\n", 127 *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL); 128 } 129 /* Only admin can change "restricted" shells. */ 130 #if 0 131 else if (ok_shell(pw->pw_shell)) 132 /* 133 * Make shell a restricted field. Ugly with a 134 * necklace, but there's not much else to do. 135 */ 136 #else 137 else if ((!list[E_SHELL].restricted && ok_shell(pw->pw_shell)) || !uid) 138 /* 139 * If change not restrict (table.c) and standard shell 140 * OR if root, then allow editing of shell. 141 */ 142 #endif 143 (void)fprintf(fp, "Shell: %s\n", 144 *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL); 145 else 146 list[E_SHELL].restricted = 1; 147 bp = pw->pw_gecos; 148 149 p = strsep(&bp, ","); 150 p = strdup(p ? p : ""); 151 list[E_NAME].save = p; 152 if (!list[E_NAME].restricted || !uid) 153 (void)fprintf(fp, "Full Name: %s\n", p); 154 155 p = strsep(&bp, ","); 156 p = strdup(p ? p : ""); 157 list[E_LOCATE].save = p; 158 if (!list[E_LOCATE].restricted || !uid) 159 (void)fprintf(fp, "Office Location: %s\n", p); 160 161 p = strsep(&bp, ","); 162 p = strdup(p ? p : ""); 163 list[E_BPHONE].save = p; 164 if (!list[E_BPHONE].restricted || !uid) 165 (void)fprintf(fp, "Office Phone: %s\n", p); 166 167 p = strsep(&bp, ","); 168 p = strdup(p ? p : ""); 169 list[E_HPHONE].save = p; 170 if (!list[E_HPHONE].restricted || !uid) 171 (void)fprintf(fp, "Home Phone: %s\n", p); 172 173 bp = strdup(bp ? bp : ""); 174 list[E_OTHER].save = bp; 175 if (!list[E_OTHER].restricted || !uid) 176 (void)fprintf(fp, "Other information: %s\n", bp); 177 178 (void)fchown(fd, getuid(), getgid()); 179 (void)fclose(fp); 180 } 181 182 int 183 verify(pw) 184 struct passwd *pw; 185 { 186 ENTRY *ep; 187 char *p; 188 struct stat sb; 189 FILE *fp; 190 int len, line; 191 static char buf[LINE_MAX]; 192 193 if (!(fp = fopen(tempname, "r"))) 194 pw_error(tempname, 1, 1); 195 if (fstat(fileno(fp), &sb)) 196 pw_error(tempname, 1, 1); 197 if (sb.st_size == 0) { 198 warnx("corrupted temporary file"); 199 goto bad; 200 } 201 line = 0; 202 while (fgets(buf, sizeof(buf), fp)) { 203 line++; 204 if (!buf[0] || buf[0] == '#') 205 continue; 206 if (!(p = strchr(buf, '\n'))) { 207 warnx("line %d too long", line); 208 goto bad; 209 } 210 *p = '\0'; 211 for (ep = list;; ++ep) { 212 if (!ep->prompt) { 213 warnx("unrecognized field on line %d", line); 214 goto bad; 215 } 216 if (!strncasecmp(buf, ep->prompt, ep->len)) { 217 if (ep->restricted && uid) { 218 warnx( 219 "you may not change the %s field", 220 ep->prompt); 221 goto bad; 222 } 223 if (!(p = strchr(buf, ':'))) { 224 warnx("line %d corrupted", line); 225 goto bad; 226 } 227 while (isspace(*++p)); 228 if (ep->except && strpbrk(p, ep->except)) { 229 warnx( 230 "illegal character in the \"%s\" field", 231 ep->prompt); 232 goto bad; 233 } 234 if ((ep->func)(p, pw, ep)) { 235 bad: (void)fclose(fp); 236 return (0); 237 } 238 break; 239 } 240 } 241 } 242 (void)fclose(fp); 243 244 /* Build the gecos field. */ 245 len = strlen(list[E_NAME].save) + strlen(list[E_BPHONE].save) + 246 strlen(list[E_HPHONE].save) + strlen(list[E_LOCATE].save) + 247 strlen(list[E_OTHER].save) + 5; 248 if (!(p = malloc(len))) 249 err(1, NULL); 250 (void)sprintf(pw->pw_gecos = p, "%s,%s,%s,%s,%s", list[E_NAME].save, 251 list[E_LOCATE].save, list[E_BPHONE].save, list[E_HPHONE].save, 252 list[E_OTHER].save); 253 254 while ((len = strlen(pw->pw_gecos)) && pw->pw_gecos[len - 1] == ',') 255 pw->pw_gecos[len - 1] = '\0'; 256 257 if (snprintf(buf, sizeof(buf), 258 "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s", 259 pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid, pw->pw_class, 260 pw->pw_change, pw->pw_expire, pw->pw_gecos, pw->pw_dir, 261 pw->pw_shell) >= sizeof(buf)) { 262 warnx("entries too long"); 263 free(p); 264 return (0); 265 } 266 free(p); 267 return (__pw_scan(buf, pw, _PWSCAN_WARN|_PWSCAN_MASTER)); 268 } 269