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