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: src/usr.bin/chpass/edit.c,v 1.16.2.2 2001/08/02 01:48:22 obrien Exp $ 34 * $DragonFly: src/usr.bin/chpass/edit.c,v 1.2 2003/06/17 04:29:25 dillon Exp $ 35 * 36 * @(#)edit.c 8.3 (Berkeley) 4/2/94 37 */ 38 39 #include <sys/param.h> 40 #include <sys/stat.h> 41 42 #include <ctype.h> 43 #include <err.h> 44 #include <errno.h> 45 #include <md5.h> 46 #include <paths.h> 47 #include <pwd.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <unistd.h> 52 53 #include <pw_scan.h> 54 #include <pw_util.h> 55 56 #include "chpass.h" 57 #ifdef YP 58 #include "pw_yp.h" 59 #endif /* YP */ 60 61 extern char *tempname; 62 63 void 64 edit(pw) 65 struct passwd *pw; 66 { 67 struct stat begin, end; 68 char *begin_sum, *end_sum; 69 70 for (;;) { 71 if (stat(tempname, &begin)) 72 pw_error(tempname, 1, 1); 73 begin_sum = MD5File(tempname, (char *)NULL); 74 pw_edit(1); 75 if (stat(tempname, &end)) 76 pw_error(tempname, 1, 1); 77 end_sum = MD5File(tempname, (char *)NULL); 78 if ((begin.st_mtime == end.st_mtime) && 79 (strcmp(begin_sum, end_sum) == 0)) { 80 warnx("no changes made"); 81 pw_error(NULL, 0, 0); 82 } 83 free(begin_sum); 84 free(end_sum); 85 if (verify(pw)) 86 break; 87 pw_prompt(); 88 } 89 } 90 91 /* 92 * display -- 93 * print out the file for the user to edit; strange side-effect: 94 * set conditional flag if the user gets to edit the shell. 95 */ 96 void 97 display(fd, pw) 98 int fd; 99 struct passwd *pw; 100 { 101 FILE *fp; 102 char *bp, *p, *ttoa(); 103 104 if (!(fp = fdopen(fd, "w"))) 105 pw_error(tempname, 1, 1); 106 107 (void)fprintf(fp, 108 #ifdef YP 109 "#Changing %s information for %s.\n", _use_yp ? "NIS" : "user database", pw->pw_name); 110 if (!uid && (!_use_yp || suser_override)) { 111 #else 112 "#Changing user database information for %s.\n", pw->pw_name); 113 if (!uid) { 114 #endif /* YP */ 115 (void)fprintf(fp, "Login: %s\n", pw->pw_name); 116 (void)fprintf(fp, "Password: %s\n", pw->pw_passwd); 117 (void)fprintf(fp, "Uid [#]: %lu\n", (unsigned long)pw->pw_uid); 118 (void)fprintf(fp, "Gid [# or name]: %lu\n", 119 (unsigned long)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:%lu:%lu:%s:%ld:%ld:%s:%s:%s", 259 pw->pw_name, pw->pw_passwd, (unsigned long)pw->pw_uid, 260 (unsigned long)pw->pw_gid, pw->pw_class, (long)pw->pw_change, 261 (long)pw->pw_expire, pw->pw_gecos, pw->pw_dir, 262 pw->pw_shell) >= sizeof(buf)) { 263 warnx("entries too long"); 264 free(p); 265 return (0); 266 } 267 free(p); 268 return (pw_scan(buf, pw)); 269 } 270