yppasswdd_server.c revision 17431
114062Swpaul/* 214062Swpaul * Copyright (c) 1995, 1996 314062Swpaul * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 414062Swpaul * 514062Swpaul * Redistribution and use in source and binary forms, with or without 614062Swpaul * modification, are permitted provided that the following conditions 714062Swpaul * are met: 814062Swpaul * 1. Redistributions of source code must retain the above copyright 914062Swpaul * notice, this list of conditions and the following disclaimer. 1014062Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1114062Swpaul * notice, this list of conditions and the following disclaimer in the 1214062Swpaul * documentation and/or other materials provided with the distribution. 1314062Swpaul * 3. All advertising materials mentioning features or use of this software 1414062Swpaul * must display the following acknowledgement: 1514062Swpaul * This product includes software developed by Bill Paul. 1614062Swpaul * 4. Neither the name of the author nor the names of any co-contributors 1714062Swpaul * may be used to endorse or promote products derived from this software 1814062Swpaul * without specific prior written permission. 1914062Swpaul * 2014062Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 2114062Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2214062Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2314062Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE 2414062Swpaul * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2514062Swpaul * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2614062Swpaul * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2714062Swpaul * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2814062Swpaul * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2914062Swpaul * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3014062Swpaul * SUCH DAMAGE. 3114062Swpaul * 3217431Swpaul * $Id: yppasswdd_server.c,v 1.6 1996/07/01 19:38:38 guido Exp $ 3314062Swpaul */ 3414062Swpaul 3514062Swpaul#include <stdio.h> 3614062Swpaul#include <string.h> 3714062Swpaul#include <ctype.h> 3814062Swpaul#include <stdlib.h> 3914062Swpaul#include <unistd.h> 4014062Swpaul#include <dirent.h> 4114062Swpaul#include <sys/stat.h> 4214062Swpaul#include <sys/socket.h> 4314062Swpaul#include <netinet/in.h> 4414062Swpaul#include <arpa/inet.h> 4514062Swpaul#include <limits.h> 4614062Swpaul#include <db.h> 4714062Swpaul#include <pwd.h> 4814062Swpaul#include <errno.h> 4914062Swpaul#include <signal.h> 5014062Swpaul#include <rpc/rpc.h> 5114062Swpaul#include <rpcsvc/yp.h> 5214062Swpaul#include <sys/types.h> 5314062Swpaul#include <sys/wait.h> 5414062Swpaul#include <sys/param.h> 5516134Swpaul#include <sys/fcntl.h> 5614062Swpaulstruct dom_binding {}; 5714062Swpaul#include <rpcsvc/ypclnt.h> 5814062Swpaul#include "yppasswdd_extern.h" 5914062Swpaul#include "yppasswd.h" 6014062Swpaul#include "yppasswd_private.h" 6114062Swpaul#include "yppasswd_comm.h" 6214062Swpaul 6314062Swpaul#ifndef lint 6417431Swpaulstatic const char rcsid[] = "$Id: yppasswdd_server.c,v 1.6 1996/07/01 19:38:38 guido Exp $"; 6514062Swpaul#endif /* not lint */ 6614062Swpaul 6714062Swpaulchar *tempname; 6814062Swpaul 6914062Swpaulvoid reaper(sig) 7014062Swpaul int sig; 7114062Swpaul{ 7214062Swpaul extern pid_t pid; 7314062Swpaul extern int pstat; 7414062Swpaul int st; 7514062Swpaul 7614062Swpaul if (sig > 0) { 7714062Swpaul if (sig == SIGCHLD) 7814062Swpaul while(wait3(&st, WNOHANG, NULL) > 0) ; 7914062Swpaul } else { 8014062Swpaul pid = waitpid(pid, &pstat, 0); 8114062Swpaul } 8214062Swpaul return; 8314062Swpaul} 8414062Swpaul 8514062Swpaulvoid install_reaper(on) 8614062Swpaul int on; 8714062Swpaul{ 8814062Swpaul if (on) { 8914062Swpaul signal(SIGCHLD, reaper); 9014062Swpaul } else { 9114062Swpaul signal(SIGCHLD, SIG_DFL); 9214062Swpaul } 9314062Swpaul return; 9414062Swpaul} 9514062Swpaul 9614062Swpaulstatic struct passwd yp_password; 9714062Swpaul 9814062Swpaulstatic void copy_yp_pass(p, x, m) 9914062Swpaulchar *p; 10014062Swpaulint x, m; 10114062Swpaul{ 10214062Swpaul register char *t, *s = p; 10314062Swpaul static char *buf; 10414062Swpaul 10514062Swpaul yp_password.pw_fields = 0; 10614062Swpaul 10714062Swpaul buf = (char *)realloc(buf, m + 10); 10814062Swpaul bzero(buf, m + 10); 10914062Swpaul 11014062Swpaul /* Turn all colons into NULLs */ 11114062Swpaul while (strchr(s, ':')) { 11214062Swpaul s = (strchr(s, ':') + 1); 11314062Swpaul *(s - 1)= '\0'; 11414062Swpaul } 11514062Swpaul 11614062Swpaul t = buf; 11714062Swpaul#define EXPAND(e) e = t; while ((*t++ = *p++)); 11814062Swpaul EXPAND(yp_password.pw_name); 11914062Swpaul yp_password.pw_fields |= _PWF_NAME; 12014062Swpaul EXPAND(yp_password.pw_passwd); 12114062Swpaul yp_password.pw_fields |= _PWF_PASSWD; 12214062Swpaul yp_password.pw_uid = atoi(p); 12314062Swpaul p += (strlen(p) + 1); 12414062Swpaul yp_password.pw_fields |= _PWF_UID; 12514062Swpaul yp_password.pw_gid = atoi(p); 12614062Swpaul p += (strlen(p) + 1); 12714062Swpaul yp_password.pw_fields |= _PWF_GID; 12814062Swpaul if (x) { 12914062Swpaul EXPAND(yp_password.pw_class); 13014062Swpaul yp_password.pw_fields |= _PWF_CLASS; 13114062Swpaul yp_password.pw_change = atol(p); 13214062Swpaul p += (strlen(p) + 1); 13314062Swpaul yp_password.pw_fields |= _PWF_CHANGE; 13414062Swpaul yp_password.pw_expire = atol(p); 13514062Swpaul p += (strlen(p) + 1); 13614062Swpaul yp_password.pw_fields |= _PWF_EXPIRE; 13714062Swpaul } 13814062Swpaul EXPAND(yp_password.pw_gecos); 13914062Swpaul yp_password.pw_fields |= _PWF_GECOS; 14014062Swpaul EXPAND(yp_password.pw_dir); 14114062Swpaul yp_password.pw_fields |= _PWF_DIR; 14214062Swpaul EXPAND(yp_password.pw_shell); 14314062Swpaul yp_password.pw_fields |= _PWF_SHELL; 14414062Swpaul 14514062Swpaul return; 14614062Swpaul} 14714062Swpaul 14814062Swpaulstatic int validchars(arg) 14914062Swpaul char *arg; 15014062Swpaul{ 15114062Swpaul int i; 15214062Swpaul 15314062Swpaul for (i = 0; i < strlen(arg); i++) { 15414062Swpaul if (iscntrl(arg[i])) { 15514062Swpaul yp_error("string contains a control character"); 15614062Swpaul return(1); 15714062Swpaul } 15814062Swpaul if (arg[i] == ':') { 15914062Swpaul yp_error("string contains a colon"); 16014062Swpaul return(1); 16114062Swpaul } 16214062Swpaul /* Be evil: truncate strings with \n in them silently. */ 16314062Swpaul if (arg[i] == '\n') { 16414062Swpaul arg[i] = '\0'; 16514062Swpaul return(0); 16614062Swpaul } 16714062Swpaul } 16814062Swpaul return(0); 16914062Swpaul} 17014062Swpaul 17114062Swpaulstatic int validate_master(opw, npw) 17214062Swpaul struct passwd *opw; 17314062Swpaul struct x_master_passwd *npw; 17414062Swpaul{ 17514062Swpaul 17614062Swpaul if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') { 17714062Swpaul yp_error("client tried to modify an NIS entry"); 17814062Swpaul return(1); 17914062Swpaul } 18014062Swpaul 18114062Swpaul if (validchars(npw->pw_shell)) { 18214062Swpaul yp_error("specified shell contains invalid characters"); 18314062Swpaul return(1); 18414062Swpaul } 18514062Swpaul 18614062Swpaul if (validchars(npw->pw_gecos)) { 18714062Swpaul yp_error("specified gecos field contains invalid characters"); 18814062Swpaul return(1); 18914062Swpaul } 19014062Swpaul 19114062Swpaul if (validchars(npw->pw_passwd)) { 19214062Swpaul yp_error("specified password contains invalid characters"); 19314062Swpaul return(1); 19414062Swpaul } 19514062Swpaul return(0); 19614062Swpaul} 19714062Swpaul 19814062Swpaulstatic int validate(opw, npw) 19914062Swpaul struct passwd *opw; 20014062Swpaul struct x_passwd *npw; 20114062Swpaul{ 20214062Swpaul 20314062Swpaul if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') { 20414062Swpaul yp_error("client tried to modify an NIS entry"); 20514062Swpaul return(1); 20614062Swpaul } 20714062Swpaul 20814062Swpaul if (npw->pw_uid != opw->pw_uid) { 20914062Swpaul yp_error("UID mismatch: client says user %s has UID %d", 21014062Swpaul npw->pw_name, npw->pw_uid); 21114062Swpaul yp_error("database says user %s has UID %d", opw->pw_name, 21214062Swpaul opw->pw_uid); 21314062Swpaul return(1); 21414062Swpaul } 21514062Swpaul 21614062Swpaul if (npw->pw_gid != opw->pw_gid) { 21714062Swpaul yp_error("GID mismatch: client says user %s has GID %d", 21814062Swpaul npw->pw_name, npw->pw_gid); 21914062Swpaul yp_error("database says user %s has GID %d", opw->pw_name, 22014062Swpaul opw->pw_gid); 22114062Swpaul return(1); 22214062Swpaul } 22314062Swpaul 22414062Swpaul /* 22514062Swpaul * Don't allow the user to shoot himself in the foot, 22614062Swpaul * even on purpose. 22714062Swpaul */ 22814062Swpaul if (!ok_shell(npw->pw_shell)) { 22914062Swpaul yp_error("%s is not a valid shell", npw->pw_shell); 23014062Swpaul return(1); 23114062Swpaul } 23214062Swpaul 23314062Swpaul if (validchars(npw->pw_shell)) { 23414062Swpaul yp_error("specified shell contains invalid characters"); 23514062Swpaul return(1); 23614062Swpaul } 23714062Swpaul 23814062Swpaul if (validchars(npw->pw_gecos)) { 23914062Swpaul yp_error("specified gecos field contains invalid characters"); 24014062Swpaul return(1); 24114062Swpaul } 24214062Swpaul 24314062Swpaul if (validchars(npw->pw_passwd)) { 24414062Swpaul yp_error("specified password contains invalid characters"); 24514062Swpaul return(1); 24614062Swpaul } 24714062Swpaul return(0); 24814062Swpaul} 24914062Swpaul 25014062Swpaul/* 25114062Swpaul * Kludge alert: 25214062Swpaul * In order to have one rpc.yppasswdd support multiple domains, 25314062Swpaul * we have to cheat: we search each directory under /var/yp 25414062Swpaul * and try to match the user in each master.passwd.byname 25514062Swpaul * map that we find. If the user matches (username, uid and gid 25614062Swpaul * all agree), then we use that domain. If we match the user in 25714062Swpaul * more than one database, we must abort. 25814062Swpaul */ 25914062Swpaulstatic char *find_domain(pw) 26014062Swpaul struct x_passwd *pw; 26114062Swpaul{ 26214062Swpaul struct stat statbuf; 26314062Swpaul struct dirent *dirp; 26414062Swpaul DIR *dird; 26514062Swpaul char yp_mapdir[MAXPATHLEN + 2]; 26615687Swpaul static char domain[YPMAXDOMAIN]; 26714062Swpaul char *tmp = NULL; 26814062Swpaul DBT key, data; 26914062Swpaul int hit = 0; 27014062Swpaul 27114062Swpaul yp_error("performing multidomain lookup"); 27214062Swpaul 27314062Swpaul if ((dird = opendir(yp_dir)) == NULL) { 27414062Swpaul yp_error("opendir(%s) failed: %s", yp_dir, strerror(errno)); 27514062Swpaul return(NULL); 27614062Swpaul } 27714062Swpaul 27814062Swpaul while ((dirp = readdir(dird)) != NULL) { 27914062Swpaul snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s", 28014062Swpaul yp_dir, dirp->d_name); 28114062Swpaul if (stat(yp_mapdir, &statbuf) < 0) { 28214062Swpaul yp_error("stat(%s) failed: %s", yp_mapdir, 28314062Swpaul strerror(errno)); 28414062Swpaul closedir(dird); 28514062Swpaul return(NULL); 28614062Swpaul } 28714062Swpaul if (S_ISDIR(statbuf.st_mode)) { 28814062Swpaul tmp = (char *)dirp->d_name; 28914062Swpaul key.data = pw->pw_name; 29014062Swpaul key.size = strlen(pw->pw_name); 29114062Swpaul 29214062Swpaul if (yp_get_record(tmp,"master.passwd.byname", 29314062Swpaul &key, &data, 0) != YP_TRUE) { 29414062Swpaul continue; 29514062Swpaul } 29614062Swpaul *(char *)(data.data + data.size) = '\0'; 29714062Swpaul copy_yp_pass(data.data, 1, data.size); 29814062Swpaul if (yp_password.pw_uid == pw->pw_uid && 29914062Swpaul yp_password.pw_gid == pw->pw_gid) { 30014062Swpaul hit++; 30115687Swpaul snprintf(domain, YPMAXDOMAIN, "%s", tmp); 30214062Swpaul } 30314062Swpaul } 30414062Swpaul } 30514062Swpaul 30614062Swpaul closedir(dird); 30714062Swpaul if (hit > 1) { 30814062Swpaul yp_error("found same user in two different domains"); 30914062Swpaul return(NULL); 31014062Swpaul } else 31116134Swpaul return((char *)&domain); 31214062Swpaul} 31314062Swpaul 31416134Swpaulstatic int update_inplace(pw, domain) 31516134Swpaul struct passwd *pw; 31616134Swpaul char *domain; 31716134Swpaul{ 31816134Swpaul DB *dbp = NULL; 31916134Swpaul DBT key = { NULL, 0 }; 32016134Swpaul DBT data = { NULL, 0 }; 32116134Swpaul char pwbuf[YPMAXRECORD]; 32216134Swpaul char keybuf[20]; 32316134Swpaul int rval, i; 32416134Swpaul char *maps[] = { "master.passwd.byname", "master.passwd.byuid", 32516134Swpaul "passwd.byname", "passwd.byuid" }; 32616134Swpaul 32716134Swpaul char *formats[] = { "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s", 32816134Swpaul "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s", 32916134Swpaul "%s:%s:%d:%d:%s:%s:%s", "%s:%s:%d:%d:%s:%s:%s" }; 33016134Swpaul char *ptr = NULL; 33116134Swpaul char *yp_last = "YP_LAST_MODIFIED"; 33216134Swpaul char yplastbuf[YPMAXRECORD]; 33316134Swpaul 33416134Swpaul snprintf(yplastbuf, sizeof(yplastbuf), "%lu", time(NULL)); 33516134Swpaul 33616134Swpaul for (i = 0; i < 4; i++) { 33716134Swpaul 33816134Swpaul if (i % 2) { 33916134Swpaul snprintf(keybuf, sizeof(keybuf), "%ld", pw->pw_uid); 34016134Swpaul key.data = (char *)&keybuf; 34116134Swpaul key.size = strlen(keybuf); 34216134Swpaul } else { 34316134Swpaul key.data = pw->pw_name; 34416134Swpaul key.size = strlen(pw->pw_name); 34516134Swpaul } 34616134Swpaul 34716134Swpaul /* 34816134Swpaul * XXX The passwd.byname and passwd.byuid maps come in 34916134Swpaul * two flavors: secure and insecure. The secure version 35016134Swpaul * has a '*' in the password field whereas the insecure one 35116134Swpaul * has a real crypted password. The maps will be insecure 35216134Swpaul * if they were built with 'unsecure = TRUE' enabled in 35316134Swpaul * /var/yp/Makefile, but we'd have no way of knowing if 35416134Swpaul * this has been done unless we were to try parsing the 35516134Swpaul * Makefile, which is a disgusting thought. Instead, we 35616134Swpaul * read the records from the maps, skip to the first ':' 35716134Swpaul * in them, and then look at the character immediately 35816134Swpaul * following it. If it's an '*' then the map is 'secure' 35916134Swpaul * and we must not insert a real password into the pw_passwd 36016134Swpaul * field. If it's not an '*', then we put the real crypted 36116134Swpaul * password in. 36216134Swpaul */ 36316134Swpaul if (yp_get_record(domain,maps[i],&key,&data,1) != YP_TRUE) { 36416134Swpaul yp_error("couldn't read %s/%s: %s", domain, 36516134Swpaul maps[i], strerror(errno)); 36616134Swpaul return(1); 36716134Swpaul } 36816134Swpaul 36916134Swpaul if ((ptr = strchr(data.data, ':')) == NULL) { 37016134Swpaul yp_error("no colon in passwd record?!"); 37116134Swpaul return(1); 37216134Swpaul } 37316134Swpaul 37416134Swpaul if (i < 2) { 37516134Swpaul snprintf(pwbuf, sizeof(pwbuf), formats[i], 37616134Swpaul pw->pw_name, pw->pw_passwd, pw->pw_uid, 37716134Swpaul pw->pw_gid, pw->pw_class, pw->pw_change, 37816134Swpaul pw->pw_expire, pw->pw_gecos, pw->pw_dir, 37916134Swpaul pw->pw_shell); 38016134Swpaul } else { 38116134Swpaul snprintf(pwbuf, sizeof(pwbuf), formats[i], 38216134Swpaul pw->pw_name, *(ptr+1) == '*' ? "*" : pw->pw_passwd, 38316134Swpaul pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir, 38416134Swpaul pw->pw_shell); 38516134Swpaul } 38616134Swpaul 38716134Swpaul#define FLAGS O_RDWR|O_CREAT 38816134Swpaul 38916134Swpaul if ((dbp = yp_open_db_rw(domain, maps[i], FLAGS)) == NULL) { 39016134Swpaul yp_error("couldn't open %s/%s r/w: %s",domain, 39116134Swpaul maps[i],strerror(errno)); 39216134Swpaul return(1); 39316134Swpaul } 39416134Swpaul 39516134Swpaul data.data = pwbuf; 39616134Swpaul data.size = strlen(pwbuf); 39716134Swpaul 39816134Swpaul if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) { 39916134Swpaul yp_error("failed to update record in %s/%s", domain, 40016134Swpaul maps[i]); 40116134Swpaul (void)(dbp->close)(dbp); 40216134Swpaul return(1); 40316134Swpaul } 40416134Swpaul 40516134Swpaul key.data = yp_last; 40616134Swpaul key.size = strlen(yp_last); 40716134Swpaul data.data = (char *)&yplastbuf; 40816134Swpaul data.size = strlen(yplastbuf); 40916134Swpaul 41016134Swpaul if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) { 41116134Swpaul yp_error("failed to update timestamp in %s/%s", domain, 41216134Swpaul maps[i]); 41316134Swpaul (void)(dbp->close)(dbp); 41416134Swpaul return(1); 41516134Swpaul } 41616134Swpaul 41716134Swpaul (void)(dbp->close)(dbp); 41816134Swpaul } 41916134Swpaul 42016134Swpaul return(0); 42116134Swpaul} 42216134Swpaul 42317431Swpaulstatic char *yp_mktmpnam() 42417431Swpaul{ 42517431Swpaul static char path[MAXPATHLEN]; 42617431Swpaul char *p; 42716134Swpaul 42817431Swpaul sprintf(path,"%s",passfile); 42917431Swpaul if ((p = strrchr(path, '/'))) 43017431Swpaul ++p; 43117431Swpaul else 43217431Swpaul p = path; 43317431Swpaul strcpy(p, "yppwtmp.XXXXXX"); 43417431Swpaul return(mktemp(path)); 43517431Swpaul} 43617431Swpaul 43714062Swpaulint * 43814062Swpaulyppasswdproc_update_1_svc(yppasswd *argp, struct svc_req *rqstp) 43914062Swpaul{ 44014062Swpaul static int result; 44114062Swpaul struct sockaddr_in *rqhost; 44214062Swpaul DBT key, data; 44314062Swpaul int rval = 0; 44414062Swpaul int pfd, tfd; 44514062Swpaul int pid; 44614062Swpaul int passwd_changed = 0; 44714062Swpaul int shell_changed = 0; 44814062Swpaul int gecos_changed = 0; 44914062Swpaul char *oldshell = NULL; 45014062Swpaul char *oldgecos = NULL; 45114062Swpaul char *passfile_hold; 45214062Swpaul char passfile_buf[MAXPATHLEN + 2]; 45314062Swpaul char *domain = yppasswd_domain; 45417431Swpaul static struct sockaddr_in clntaddr; 45517431Swpaul static struct timeval t_saved, t_test; 45614062Swpaul 45714062Swpaul /* 45814062Swpaul * Normal user updates always use the 'default' master.passwd file. 45914062Swpaul */ 46014062Swpaul 46114062Swpaul passfile = passfile_default; 46214062Swpaul result = 1; 46314062Swpaul 46414062Swpaul rqhost = svc_getcaller(rqstp->rq_xprt); 46514062Swpaul 46617431Swpaul gettimeofday(&t_test, NULL); 46717431Swpaul if (!bcmp((char *)rqhost, (char *)&clntaddr, 46817431Swpaul sizeof(struct sockaddr_in)) && 46917431Swpaul t_test.tv_sec > t_saved.tv_sec && 47017431Swpaul t_test.tv_sec - t_saved.tv_sec < 300) { 47117431Swpaul 47217431Swpaul bzero((char *)&clntaddr, sizeof(struct sockaddr_in)); 47317431Swpaul bzero((char *)&t_saved, sizeof(struct timeval)); 47417431Swpaul return(NULL); 47517431Swpaul } 47617431Swpaul 47717431Swpaul bcopy((char *)rqhost, (char *)&clntaddr, sizeof(struct sockaddr_in)); 47817431Swpaul gettimeofday(&t_saved, NULL); 47917431Swpaul 48014241Swpaul if (yp_access(resvport ? "master.passwd.byname" : NULL, rqstp)) { 48114241Swpaul yp_error("rejected update request from unauthorized host"); 48214241Swpaul svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED); 48314241Swpaul return(&result); 48414241Swpaul } 48514241Swpaul 48614062Swpaul /* 48714062Swpaul * Step one: find the user. (It's kinda pointless to 48814062Swpaul * proceed if the user doesn't exist.) We look for the 48914062Swpaul * user in the master.passwd.byname database, _NOT_ by 49014062Swpaul * using getpwent() and friends! We can't use getpwent() 49114062Swpaul * since the NIS master server is not guaranteed to be 49214062Swpaul * configured as an NIS client. 49314062Swpaul */ 49414062Swpaul 49514062Swpaul if (multidomain) { 49614062Swpaul if ((domain = find_domain(&argp->newpw)) == NULL) { 49714062Swpaul yp_error("multidomain lookup failed - aborting update"); 49814062Swpaul return(&result); 49914062Swpaul } else 50014062Swpaul yp_error("updating user %s in domain %s", 50114062Swpaul argp->newpw.pw_name, domain); 50214062Swpaul } 50314062Swpaul 50414062Swpaul key.data = argp->newpw.pw_name; 50514062Swpaul key.size = strlen(argp->newpw.pw_name); 50614062Swpaul 50714062Swpaul if ((rval=yp_get_record(domain,"master.passwd.byname", 50814062Swpaul &key, &data, 0)) != YP_TRUE) { 50914062Swpaul if (rval == YP_NOKEY) { 51014062Swpaul yp_error("user %s not found in passwd database", 51114062Swpaul argp->newpw.pw_name); 51214062Swpaul } else { 51314062Swpaul yp_error("database access error: %s", 51414062Swpaul yperr_string(rval)); 51514062Swpaul } 51614062Swpaul return(&result); 51714062Swpaul } 51814062Swpaul 51914062Swpaul /* Nul terminate, please. */ 52014062Swpaul *(char *)(data.data + data.size) = '\0'; 52114062Swpaul 52214062Swpaul copy_yp_pass(data.data, 1, data.size); 52314062Swpaul 52414062Swpaul /* Step 2: check that the supplied oldpass is valid. */ 52514062Swpaul 52614062Swpaul if (strcmp(crypt(argp->oldpass, yp_password.pw_passwd), 52714062Swpaul yp_password.pw_passwd)) { 52814062Swpaul yp_error("rejected change attempt -- bad password"); 52914062Swpaul yp_error("client address: %s username: %s", 53014062Swpaul inet_ntoa(rqhost->sin_addr), 53114062Swpaul argp->newpw.pw_name); 53214062Swpaul return(&result); 53314062Swpaul } 53414062Swpaul 53514062Swpaul /* Step 3: validate the arguments passed to us by the client. */ 53614062Swpaul 53714062Swpaul if (validate(&yp_password, &argp->newpw)) { 53814062Swpaul yp_error("rejecting change attempt: bad arguments"); 53914062Swpaul yp_error("client address: %s username: %s", 54014062Swpaul inet_ntoa(rqhost->sin_addr), 54114062Swpaul argp->newpw.pw_name); 54214062Swpaul svcerr_decode(rqstp->rq_xprt); 54314062Swpaul return(&result); 54414062Swpaul } 54514062Swpaul 54614062Swpaul /* Step 4: update the user's passwd structure. */ 54714062Swpaul 54814062Swpaul if (!no_chsh && strcmp(argp->newpw.pw_shell, yp_password.pw_shell)) { 54914062Swpaul oldshell = yp_password.pw_shell; 55014062Swpaul yp_password.pw_shell = argp->newpw.pw_shell; 55114062Swpaul shell_changed++; 55214062Swpaul } 55314062Swpaul 55414062Swpaul 55514062Swpaul if (!no_chfn && strcmp(argp->newpw.pw_gecos, yp_password.pw_gecos)) { 55614062Swpaul oldgecos = yp_password.pw_gecos; 55714062Swpaul yp_password.pw_gecos = argp->newpw.pw_gecos; 55814062Swpaul gecos_changed++; 55914062Swpaul } 56014062Swpaul 56114062Swpaul if (strcmp(argp->newpw.pw_passwd, yp_password.pw_passwd)) { 56214062Swpaul yp_password.pw_passwd = argp->newpw.pw_passwd; 56314062Swpaul passwd_changed++; 56414062Swpaul } 56514062Swpaul 56614062Swpaul /* 56714062Swpaul * If the caller specified a domain other than our 'default' 56814062Swpaul * domain, change the path to master.passwd accordingly. 56914062Swpaul */ 57014062Swpaul 57114062Swpaul if (strcmp(domain, yppasswd_domain)) { 57214062Swpaul snprintf(passfile_buf, sizeof(passfile_buf), 57316649Swpaul "%s/%s/master.passwd", yp_dir, domain); 57414062Swpaul passfile = (char *)&passfile_buf; 57514062Swpaul } 57614062Swpaul 57714062Swpaul /* Step 5: make a new password file with the updated info. */ 57814062Swpaul 57914062Swpaul if ((pfd = pw_lock()) < 0) { 58014062Swpaul return (&result); 58114062Swpaul } 58214062Swpaul if ((tfd = pw_tmp()) < 0) { 58314062Swpaul return (&result); 58414062Swpaul } 58514062Swpaul 58614062Swpaul if (pw_copy(pfd, tfd, &yp_password)) { 58714062Swpaul yp_error("failed to created updated password file -- \ 58814062Swpaulcleaning up and bailing out"); 58914062Swpaul unlink(tempname); 59014062Swpaul return(&result); 59114062Swpaul } 59214062Swpaul 59317431Swpaul passfile_hold = yp_mktmpnam(); 59414062Swpaul rename(passfile, passfile_hold); 59514062Swpaul if (strcmp(passfile, _PATH_MASTERPASSWD)) { 59614062Swpaul rename(tempname, passfile); 59714062Swpaul } else { 59816876Sguido if (pw_mkdb(argp->newpw.pw_name) < 0) { 59914062Swpaul yp_error("pwd_mkdb failed"); 60014062Swpaul return(&result); 60114062Swpaul } 60214062Swpaul } 60314062Swpaul 60416134Swpaul if (inplace) { 60516134Swpaul if ((rval = update_inplace(&yp_password, domain))) { 60616134Swpaul yp_error("inplace update failed -- rebuilding maps"); 60716134Swpaul } 60816134Swpaul } 60916134Swpaul 61014062Swpaul switch((pid = fork())) { 61114062Swpaul case 0: 61216134Swpaul if (inplace && !rval) { 61316134Swpaul execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 61416134Swpaul yppasswd_domain, "pushpw", NULL); 61516134Swpaul } else { 61616134Swpaul execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 61716134Swpaul yppasswd_domain, NULL); 61816134Swpaul } 61914062Swpaul yp_error("couldn't exec map update process: %s", 62014062Swpaul strerror(errno)); 62114062Swpaul unlink(passfile); 62214062Swpaul rename(passfile_hold, passfile); 62314062Swpaul exit(1); 62414062Swpaul break; 62514062Swpaul case -1: 62614062Swpaul yp_error("fork() failed: %s", strerror(errno)); 62714062Swpaul unlink(passfile); 62814062Swpaul rename(passfile_hold, passfile); 62917431Swpaul return(&result); 63014062Swpaul break; 63114062Swpaul default: 63217431Swpaul unlink(passfile_hold); 63314062Swpaul break; 63414062Swpaul } 63514062Swpaul 63614062Swpaul if (verbose) { 63714062Swpaul yp_error("update completed for user %s (uid %d):", 63814062Swpaul argp->newpw.pw_name, 63914062Swpaul argp->newpw.pw_uid); 64014062Swpaul 64114062Swpaul if (passwd_changed) 64214062Swpaul yp_error("password changed"); 64314062Swpaul 64414062Swpaul if (gecos_changed) 64514062Swpaul yp_error("gecos changed ('%s' -> '%s')", 64614062Swpaul oldgecos, argp->newpw.pw_gecos); 64714062Swpaul 64814062Swpaul if (shell_changed) 64914062Swpaul yp_error("shell changed ('%s' -> '%s')", 65014062Swpaul oldshell, argp->newpw.pw_shell); 65114062Swpaul } 65214062Swpaul 65314062Swpaul result = 0; 65414062Swpaul return (&result); 65514062Swpaul} 65614062Swpaul 65714062Swpaul/* 65814062Swpaul * Note that this function performs a little less sanity checking 65914062Swpaul * than the last one. Since only the superuser is allowed to use it, 66014062Swpaul * it is assumed that the caller knows what he's doing. 66114062Swpaul */ 66214062Swpaulstatic int update_master(master_yppasswd *argp) 66314062Swpaul{ 66414062Swpaul int result; 66514062Swpaul int pfd, tfd; 66614062Swpaul int pid; 66714062Swpaul int rval = 0; 66814062Swpaul DBT key, data; 66914062Swpaul char *passfile_hold; 67014062Swpaul char passfile_buf[MAXPATHLEN + 2]; 67114062Swpaul 67214062Swpaul result = 1; 67314062Swpaul passfile = passfile_default; 67414062Swpaul 67514062Swpaul key.data = argp->newpw.pw_name; 67614062Swpaul key.size = strlen(argp->newpw.pw_name); 67714062Swpaul 67814062Swpaul /* 67914062Swpaul * The superuser may add entries to the passwd maps if 68014062Swpaul * rpc.yppasswdd is started with the -a flag. Paranoia 68114062Swpaul * prevents me from allowing additions by default. 68214062Swpaul */ 68314062Swpaul if ((rval = yp_get_record(argp->domain, "master.passwd.byname", 68414062Swpaul &key, &data, 0)) != YP_TRUE) { 68514062Swpaul if (rval == YP_NOKEY) { 68614062Swpaul yp_error("user %s not found in passwd database", 68714062Swpaul argp->newpw.pw_name); 68814062Swpaul if (allow_additions) 68914062Swpaul yp_error("notice: adding user %s to \ 69014062Swpaulmaster.passwd database for domain %s", argp->newpw.pw_name, argp->domain); 69114062Swpaul else 69214062Swpaul yp_error("restart %s with the -a flag to \ 69314062Swpaulallow additions to be made to the password database", progname); 69414062Swpaul } else { 69514062Swpaul yp_error("database access error: %s", 69614062Swpaul yperr_string(rval)); 69714062Swpaul } 69814062Swpaul if (!allow_additions) 69914062Swpaul return(result); 70014062Swpaul } else { 70114062Swpaul 70214062Swpaul /* Nul terminate, please. */ 70314062Swpaul *(char *)(data.data + data.size) = '\0'; 70414062Swpaul 70514062Swpaul copy_yp_pass(data.data, 1, data.size); 70614062Swpaul } 70714062Swpaul 70814062Swpaul /* 70914062Swpaul * Perform a small bit of sanity checking. 71014062Swpaul */ 71114062Swpaul if (validate_master(rval == YP_TRUE ? &yp_password:NULL,&argp->newpw)){ 71214062Swpaul yp_error("rejecting update attempt for %s: bad arguments", 71314062Swpaul argp->newpw.pw_name); 71414062Swpaul return(result); 71514062Swpaul } 71614062Swpaul 71714062Swpaul /* 71814062Swpaul * If the caller specified a domain other than our 'default' 71914062Swpaul * domain, change the path to master.passwd accordingly. 72014062Swpaul */ 72114062Swpaul 72214062Swpaul if (strcmp(argp->domain, yppasswd_domain)) { 72314062Swpaul snprintf(passfile_buf, sizeof(passfile_buf), 72416649Swpaul "%s/%s/master.passwd", yp_dir, argp->domain); 72514062Swpaul passfile = (char *)&passfile_buf; 72614062Swpaul } 72714062Swpaul 72814062Swpaul if ((pfd = pw_lock()) < 0) { 72914062Swpaul return (result); 73014062Swpaul } 73114062Swpaul if ((tfd = pw_tmp()) < 0) { 73214062Swpaul return (result); 73314062Swpaul } 73414062Swpaul 73514062Swpaul if (pw_copy(pfd, tfd, (struct passwd *)&argp->newpw)) { 73614062Swpaul yp_error("failed to created updated password file -- \ 73714062Swpaulcleaning up and bailing out"); 73814062Swpaul unlink(tempname); 73914062Swpaul return(result); 74014062Swpaul } 74114062Swpaul 74217431Swpaul passfile_hold = yp_mktmpnam(); 74314062Swpaul rename(passfile, passfile_hold); 74414062Swpaul if (strcmp(passfile, _PATH_MASTERPASSWD)) { 74514062Swpaul rename(tempname, passfile); 74614062Swpaul } else { 74716876Sguido if (pw_mkdb(argp->newpw.pw_name) < 0) { 74814062Swpaul yp_error("pwd_mkdb failed"); 74914062Swpaul return(result); 75014062Swpaul } 75114062Swpaul } 75214062Swpaul 75316134Swpaul if (inplace) { 75416134Swpaul if ((rval = update_inplace((struct passwd *)&argp->newpw, 75516134Swpaul argp->domain))) { 75616134Swpaul yp_error("inplace update failed -- rebuilding maps"); 75716134Swpaul } 75816134Swpaul } 75916134Swpaul 76014062Swpaul switch((pid = fork())) { 76114062Swpaul case 0: 76214062Swpaul close(yp_sock); 76316134Swpaul if (inplace && !rval) { 76416134Swpaul execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 76516134Swpaul argp->domain, "pushpw", NULL); 76616134Swpaul } else { 76716134Swpaul execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 76816134Swpaul argp->domain, NULL); 76916134Swpaul } 77014062Swpaul yp_error("couldn't exec map update process: %s", 77114062Swpaul strerror(errno)); 77214062Swpaul unlink(passfile); 77314062Swpaul rename(passfile_hold, passfile); 77414062Swpaul exit(1); 77514062Swpaul break; 77614062Swpaul case -1: 77714062Swpaul yp_error("fork() failed: %s", strerror(errno)); 77814062Swpaul unlink(passfile); 77914062Swpaul rename(passfile_hold, passfile); 78014062Swpaul return(result); 78114062Swpaul break; 78214062Swpaul default: 78317431Swpaul unlink(passfile_hold); 78414062Swpaul break; 78514062Swpaul } 78614062Swpaul 78714062Swpaul yp_error("performed update of user %s (uid %d) domain %s", 78814062Swpaul argp->newpw.pw_name, 78914062Swpaul argp->newpw.pw_uid, 79014062Swpaul argp->domain); 79114062Swpaul 79214062Swpaul result = 0; 79314062Swpaul return(result); 79414062Swpaul} 79514062Swpaul 79614062Swpaul/* 79714062Swpaul * Pseudo-dispatcher for private 'superuser-only' update handler. 79814062Swpaul */ 79914062Swpaulvoid do_master() 80014062Swpaul{ 80114062Swpaul struct master_yppasswd *pw; 80214062Swpaul 80314062Swpaul if ((pw = getdat(yp_sock)) == NULL) { 80414062Swpaul return; 80514062Swpaul } 80614062Swpaul 80714062Swpaul yp_error("received update request from superuser on localhost"); 80815687Swpaul sendresp(update_master(pw)); 80914062Swpaul 81014062Swpaul /* Remember to free args. */ 81114062Swpaul xdr_free(xdr_master_yppasswd, (char *)pw); 81214062Swpaul 81314062Swpaul return; 81414062Swpaul} 815