yppasswdd_server.c revision 79452
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 */ 3214062Swpaul 3330377Scharnier#ifndef lint 3430377Scharnierstatic const char rcsid[] = 3550479Speter "$FreeBSD: head/usr.sbin/rpc.yppasswdd/yppasswdd_server.c 79452 2001-07-09 09:24:06Z brian $"; 3630377Scharnier#endif /* not lint */ 3730377Scharnier 3814062Swpaul#include <stdio.h> 3914062Swpaul#include <string.h> 4014062Swpaul#include <ctype.h> 4114062Swpaul#include <stdlib.h> 4214062Swpaul#include <unistd.h> 4314062Swpaul#include <dirent.h> 4414062Swpaul#include <sys/stat.h> 4514062Swpaul#include <sys/socket.h> 4614062Swpaul#include <netinet/in.h> 4714062Swpaul#include <arpa/inet.h> 4814062Swpaul#include <limits.h> 4914062Swpaul#include <db.h> 5014062Swpaul#include <pwd.h> 5114062Swpaul#include <errno.h> 5214062Swpaul#include <signal.h> 5314062Swpaul#include <rpc/rpc.h> 5414062Swpaul#include <rpcsvc/yp.h> 5514062Swpaul#include <sys/types.h> 5614062Swpaul#include <sys/wait.h> 5714062Swpaul#include <sys/param.h> 5816134Swpaul#include <sys/fcntl.h> 5914062Swpaulstruct dom_binding {}; 6014062Swpaul#include <rpcsvc/ypclnt.h> 6114062Swpaul#include "yppasswdd_extern.h" 6214062Swpaul#include "yppasswd.h" 6314062Swpaul#include "yppasswd_private.h" 6414062Swpaul 6574660Salfredstruct cmessage { 6674660Salfred struct cmsghdr cmsg; 6774660Salfred struct cmsgcred cmcred; 6874660Salfred}; 6974660Salfred 7014062Swpaulchar *tempname; 7114062Swpaul 7214062Swpaulvoid reaper(sig) 7314062Swpaul int sig; 7414062Swpaul{ 7514062Swpaul extern pid_t pid; 7614062Swpaul extern int pstat; 7714062Swpaul int st; 7836639Swpaul int saved_errno; 7914062Swpaul 8036639Swpaul saved_errno = errno; 8136639Swpaul 8214062Swpaul if (sig > 0) { 8314062Swpaul if (sig == SIGCHLD) 8414062Swpaul while(wait3(&st, WNOHANG, NULL) > 0) ; 8514062Swpaul } else { 8614062Swpaul pid = waitpid(pid, &pstat, 0); 8714062Swpaul } 8836639Swpaul 8936639Swpaul errno = saved_errno; 9014062Swpaul return; 9114062Swpaul} 9214062Swpaul 9314062Swpaulvoid install_reaper(on) 9414062Swpaul int on; 9514062Swpaul{ 9614062Swpaul if (on) { 9714062Swpaul signal(SIGCHLD, reaper); 9814062Swpaul } else { 9914062Swpaul signal(SIGCHLD, SIG_DFL); 10014062Swpaul } 10114062Swpaul return; 10214062Swpaul} 10314062Swpaul 10414062Swpaulstatic struct passwd yp_password; 10514062Swpaul 10614062Swpaulstatic void copy_yp_pass(p, x, m) 10714062Swpaulchar *p; 10814062Swpaulint x, m; 10914062Swpaul{ 11014062Swpaul register char *t, *s = p; 11114062Swpaul static char *buf; 11214062Swpaul 11314062Swpaul yp_password.pw_fields = 0; 11414062Swpaul 11514062Swpaul buf = (char *)realloc(buf, m + 10); 11614062Swpaul bzero(buf, m + 10); 11714062Swpaul 11814062Swpaul /* Turn all colons into NULLs */ 11914062Swpaul while (strchr(s, ':')) { 12014062Swpaul s = (strchr(s, ':') + 1); 12114062Swpaul *(s - 1)= '\0'; 12214062Swpaul } 12314062Swpaul 12414062Swpaul t = buf; 12514062Swpaul#define EXPAND(e) e = t; while ((*t++ = *p++)); 12614062Swpaul EXPAND(yp_password.pw_name); 12714062Swpaul yp_password.pw_fields |= _PWF_NAME; 12814062Swpaul EXPAND(yp_password.pw_passwd); 12914062Swpaul yp_password.pw_fields |= _PWF_PASSWD; 13014062Swpaul yp_password.pw_uid = atoi(p); 13114062Swpaul p += (strlen(p) + 1); 13214062Swpaul yp_password.pw_fields |= _PWF_UID; 13314062Swpaul yp_password.pw_gid = atoi(p); 13414062Swpaul p += (strlen(p) + 1); 13514062Swpaul yp_password.pw_fields |= _PWF_GID; 13614062Swpaul if (x) { 13714062Swpaul EXPAND(yp_password.pw_class); 13814062Swpaul yp_password.pw_fields |= _PWF_CLASS; 13914062Swpaul yp_password.pw_change = atol(p); 14014062Swpaul p += (strlen(p) + 1); 14114062Swpaul yp_password.pw_fields |= _PWF_CHANGE; 14214062Swpaul yp_password.pw_expire = atol(p); 14314062Swpaul p += (strlen(p) + 1); 14414062Swpaul yp_password.pw_fields |= _PWF_EXPIRE; 14514062Swpaul } 14614062Swpaul EXPAND(yp_password.pw_gecos); 14714062Swpaul yp_password.pw_fields |= _PWF_GECOS; 14814062Swpaul EXPAND(yp_password.pw_dir); 14914062Swpaul yp_password.pw_fields |= _PWF_DIR; 15014062Swpaul EXPAND(yp_password.pw_shell); 15114062Swpaul yp_password.pw_fields |= _PWF_SHELL; 15214062Swpaul 15314062Swpaul return; 15414062Swpaul} 15514062Swpaul 15614062Swpaulstatic int validchars(arg) 15714062Swpaul char *arg; 15814062Swpaul{ 15914062Swpaul int i; 16014062Swpaul 16114062Swpaul for (i = 0; i < strlen(arg); i++) { 16214062Swpaul if (iscntrl(arg[i])) { 16314062Swpaul yp_error("string contains a control character"); 16414062Swpaul return(1); 16514062Swpaul } 16614062Swpaul if (arg[i] == ':') { 16714062Swpaul yp_error("string contains a colon"); 16814062Swpaul return(1); 16914062Swpaul } 17014062Swpaul /* Be evil: truncate strings with \n in them silently. */ 17114062Swpaul if (arg[i] == '\n') { 17214062Swpaul arg[i] = '\0'; 17314062Swpaul return(0); 17414062Swpaul } 17514062Swpaul } 17614062Swpaul return(0); 17714062Swpaul} 17814062Swpaul 17914062Swpaulstatic int validate_master(opw, npw) 18014062Swpaul struct passwd *opw; 18114062Swpaul struct x_master_passwd *npw; 18214062Swpaul{ 18314062Swpaul 18414062Swpaul if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') { 18514062Swpaul yp_error("client tried to modify an NIS entry"); 18614062Swpaul return(1); 18714062Swpaul } 18814062Swpaul 18914062Swpaul if (validchars(npw->pw_shell)) { 19014062Swpaul yp_error("specified shell contains invalid characters"); 19114062Swpaul return(1); 19214062Swpaul } 19314062Swpaul 19414062Swpaul if (validchars(npw->pw_gecos)) { 19514062Swpaul yp_error("specified gecos field contains invalid characters"); 19614062Swpaul return(1); 19714062Swpaul } 19814062Swpaul 19914062Swpaul if (validchars(npw->pw_passwd)) { 20014062Swpaul yp_error("specified password contains invalid characters"); 20114062Swpaul return(1); 20214062Swpaul } 20314062Swpaul return(0); 20414062Swpaul} 20514062Swpaul 20614062Swpaulstatic int validate(opw, npw) 20714062Swpaul struct passwd *opw; 20814062Swpaul struct x_passwd *npw; 20914062Swpaul{ 21014062Swpaul 21114062Swpaul if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') { 21214062Swpaul yp_error("client tried to modify an NIS entry"); 21314062Swpaul return(1); 21414062Swpaul } 21514062Swpaul 21614062Swpaul if (npw->pw_uid != opw->pw_uid) { 21714062Swpaul yp_error("UID mismatch: client says user %s has UID %d", 21814062Swpaul npw->pw_name, npw->pw_uid); 21914062Swpaul yp_error("database says user %s has UID %d", opw->pw_name, 22014062Swpaul opw->pw_uid); 22114062Swpaul return(1); 22214062Swpaul } 22314062Swpaul 22414062Swpaul if (npw->pw_gid != opw->pw_gid) { 22514062Swpaul yp_error("GID mismatch: client says user %s has GID %d", 22614062Swpaul npw->pw_name, npw->pw_gid); 22714062Swpaul yp_error("database says user %s has GID %d", opw->pw_name, 22814062Swpaul opw->pw_gid); 22914062Swpaul return(1); 23014062Swpaul } 23114062Swpaul 23214062Swpaul /* 23314062Swpaul * Don't allow the user to shoot himself in the foot, 23414062Swpaul * even on purpose. 23514062Swpaul */ 23614062Swpaul if (!ok_shell(npw->pw_shell)) { 23714062Swpaul yp_error("%s is not a valid shell", npw->pw_shell); 23814062Swpaul return(1); 23914062Swpaul } 24014062Swpaul 24114062Swpaul if (validchars(npw->pw_shell)) { 24214062Swpaul yp_error("specified shell contains invalid characters"); 24314062Swpaul return(1); 24414062Swpaul } 24514062Swpaul 24614062Swpaul if (validchars(npw->pw_gecos)) { 24714062Swpaul yp_error("specified gecos field contains invalid characters"); 24814062Swpaul return(1); 24914062Swpaul } 25014062Swpaul 25114062Swpaul if (validchars(npw->pw_passwd)) { 25214062Swpaul yp_error("specified password contains invalid characters"); 25314062Swpaul return(1); 25414062Swpaul } 25514062Swpaul return(0); 25614062Swpaul} 25714062Swpaul 25814062Swpaul/* 25914062Swpaul * Kludge alert: 26014062Swpaul * In order to have one rpc.yppasswdd support multiple domains, 26114062Swpaul * we have to cheat: we search each directory under /var/yp 26214062Swpaul * and try to match the user in each master.passwd.byname 26314062Swpaul * map that we find. If the user matches (username, uid and gid 26414062Swpaul * all agree), then we use that domain. If we match the user in 26514062Swpaul * more than one database, we must abort. 26614062Swpaul */ 26714062Swpaulstatic char *find_domain(pw) 26814062Swpaul struct x_passwd *pw; 26914062Swpaul{ 27014062Swpaul struct stat statbuf; 27114062Swpaul struct dirent *dirp; 27214062Swpaul DIR *dird; 27314062Swpaul char yp_mapdir[MAXPATHLEN + 2]; 27415687Swpaul static char domain[YPMAXDOMAIN]; 27514062Swpaul char *tmp = NULL; 27614062Swpaul DBT key, data; 27714062Swpaul int hit = 0; 27814062Swpaul 27914062Swpaul yp_error("performing multidomain lookup"); 28014062Swpaul 28114062Swpaul if ((dird = opendir(yp_dir)) == NULL) { 28214062Swpaul yp_error("opendir(%s) failed: %s", yp_dir, strerror(errno)); 28314062Swpaul return(NULL); 28414062Swpaul } 28514062Swpaul 28614062Swpaul while ((dirp = readdir(dird)) != NULL) { 28714062Swpaul snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s", 28814062Swpaul yp_dir, dirp->d_name); 28914062Swpaul if (stat(yp_mapdir, &statbuf) < 0) { 29014062Swpaul yp_error("stat(%s) failed: %s", yp_mapdir, 29114062Swpaul strerror(errno)); 29214062Swpaul closedir(dird); 29314062Swpaul return(NULL); 29414062Swpaul } 29514062Swpaul if (S_ISDIR(statbuf.st_mode)) { 29614062Swpaul tmp = (char *)dirp->d_name; 29714062Swpaul key.data = pw->pw_name; 29814062Swpaul key.size = strlen(pw->pw_name); 29914062Swpaul 30014062Swpaul if (yp_get_record(tmp,"master.passwd.byname", 30114062Swpaul &key, &data, 0) != YP_TRUE) { 30214062Swpaul continue; 30314062Swpaul } 30414062Swpaul *(char *)(data.data + data.size) = '\0'; 30514062Swpaul copy_yp_pass(data.data, 1, data.size); 30614062Swpaul if (yp_password.pw_uid == pw->pw_uid && 30714062Swpaul yp_password.pw_gid == pw->pw_gid) { 30814062Swpaul hit++; 30915687Swpaul snprintf(domain, YPMAXDOMAIN, "%s", tmp); 31014062Swpaul } 31114062Swpaul } 31214062Swpaul } 31314062Swpaul 31414062Swpaul closedir(dird); 31514062Swpaul if (hit > 1) { 31614062Swpaul yp_error("found same user in two different domains"); 31714062Swpaul return(NULL); 31814062Swpaul } else 31916134Swpaul return((char *)&domain); 32014062Swpaul} 32114062Swpaul 32216134Swpaulstatic int update_inplace(pw, domain) 32316134Swpaul struct passwd *pw; 32416134Swpaul char *domain; 32516134Swpaul{ 32616134Swpaul DB *dbp = NULL; 32716134Swpaul DBT key = { NULL, 0 }; 32816134Swpaul DBT data = { NULL, 0 }; 32916134Swpaul char pwbuf[YPMAXRECORD]; 33016134Swpaul char keybuf[20]; 33119778Speter int i; 33216134Swpaul char *maps[] = { "master.passwd.byname", "master.passwd.byuid", 33316134Swpaul "passwd.byname", "passwd.byuid" }; 33416134Swpaul 33516134Swpaul char *formats[] = { "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s", 33616134Swpaul "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s", 33716134Swpaul "%s:%s:%d:%d:%s:%s:%s", "%s:%s:%d:%d:%s:%s:%s" }; 33816134Swpaul char *ptr = NULL; 33916134Swpaul char *yp_last = "YP_LAST_MODIFIED"; 34016134Swpaul char yplastbuf[YPMAXRECORD]; 34116134Swpaul 34216134Swpaul snprintf(yplastbuf, sizeof(yplastbuf), "%lu", time(NULL)); 34316134Swpaul 34416134Swpaul for (i = 0; i < 4; i++) { 34516134Swpaul 34616134Swpaul if (i % 2) { 34716134Swpaul snprintf(keybuf, sizeof(keybuf), "%ld", pw->pw_uid); 34816134Swpaul key.data = (char *)&keybuf; 34916134Swpaul key.size = strlen(keybuf); 35016134Swpaul } else { 35116134Swpaul key.data = pw->pw_name; 35216134Swpaul key.size = strlen(pw->pw_name); 35316134Swpaul } 35416134Swpaul 35516134Swpaul /* 35616134Swpaul * XXX The passwd.byname and passwd.byuid maps come in 35716134Swpaul * two flavors: secure and insecure. The secure version 35816134Swpaul * has a '*' in the password field whereas the insecure one 35916134Swpaul * has a real crypted password. The maps will be insecure 36016134Swpaul * if they were built with 'unsecure = TRUE' enabled in 36116134Swpaul * /var/yp/Makefile, but we'd have no way of knowing if 36216134Swpaul * this has been done unless we were to try parsing the 36316134Swpaul * Makefile, which is a disgusting thought. Instead, we 36416134Swpaul * read the records from the maps, skip to the first ':' 36516134Swpaul * in them, and then look at the character immediately 36616134Swpaul * following it. If it's an '*' then the map is 'secure' 36716134Swpaul * and we must not insert a real password into the pw_passwd 36816134Swpaul * field. If it's not an '*', then we put the real crypted 36916134Swpaul * password in. 37016134Swpaul */ 37116134Swpaul if (yp_get_record(domain,maps[i],&key,&data,1) != YP_TRUE) { 37216134Swpaul yp_error("couldn't read %s/%s: %s", domain, 37316134Swpaul maps[i], strerror(errno)); 37416134Swpaul return(1); 37516134Swpaul } 37616134Swpaul 37716134Swpaul if ((ptr = strchr(data.data, ':')) == NULL) { 37816134Swpaul yp_error("no colon in passwd record?!"); 37916134Swpaul return(1); 38016134Swpaul } 38116134Swpaul 38219138Swpaul /* 38319138Swpaul * XXX Supposing we have more than one user with the same 38419138Swpaul * UID? (Or more than one user with the same name?) We could 38519138Swpaul * end up modifying the wrong record if were not careful. 38619138Swpaul */ 38719138Swpaul if (i % 2) { 38819138Swpaul if (strncmp(data.data, pw->pw_name, 38919138Swpaul strlen(pw->pw_name))) { 39019138Swpaul yp_error("warning: found entry for UID %d \ 39119138Swpaulin map %s@%s with wrong name (%.*s)", pw->pw_uid, maps[i], domain, 39219138Swpaul ptr - (char *)data.data, data.data); 39319138Swpaul yp_error("there may be more than one user \ 39419138Swpaulwith the same UID - continuing"); 39519138Swpaul continue; 39619138Swpaul } 39719138Swpaul } else { 39819138Swpaul /* 39919138Swpaul * We're really being ultra-paranoid here. 40019138Swpaul * This is generally a 'can't happen' condition. 40119138Swpaul */ 40219138Swpaul snprintf(pwbuf, sizeof(pwbuf), ":%d:%d:", pw->pw_uid, 40319138Swpaul pw->pw_gid); 40419138Swpaul if (!strstr(data.data, pwbuf)) { 40519138Swpaul yp_error("warning: found entry for user %s \ 40619138Swpaulin map %s@%s with wrong UID", pw->pw_name, maps[i], domain); 40778369Sdd yp_error("there may be more than one user 40819138Swpaulwith the same name - continuing"); 40919138Swpaul continue; 41019138Swpaul } 41119138Swpaul } 41219138Swpaul 41316134Swpaul if (i < 2) { 41416134Swpaul snprintf(pwbuf, sizeof(pwbuf), formats[i], 41516134Swpaul pw->pw_name, pw->pw_passwd, pw->pw_uid, 41616134Swpaul pw->pw_gid, pw->pw_class, pw->pw_change, 41716134Swpaul pw->pw_expire, pw->pw_gecos, pw->pw_dir, 41816134Swpaul pw->pw_shell); 41916134Swpaul } else { 42016134Swpaul snprintf(pwbuf, sizeof(pwbuf), formats[i], 42116134Swpaul pw->pw_name, *(ptr+1) == '*' ? "*" : pw->pw_passwd, 42216134Swpaul pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir, 42316134Swpaul pw->pw_shell); 42416134Swpaul } 42516134Swpaul 42616134Swpaul#define FLAGS O_RDWR|O_CREAT 42716134Swpaul 42816134Swpaul if ((dbp = yp_open_db_rw(domain, maps[i], FLAGS)) == NULL) { 42916134Swpaul yp_error("couldn't open %s/%s r/w: %s",domain, 43016134Swpaul maps[i],strerror(errno)); 43116134Swpaul return(1); 43216134Swpaul } 43316134Swpaul 43416134Swpaul data.data = pwbuf; 43516134Swpaul data.size = strlen(pwbuf); 43616134Swpaul 43716134Swpaul if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) { 43816134Swpaul yp_error("failed to update record in %s/%s", domain, 43916134Swpaul maps[i]); 44016134Swpaul (void)(dbp->close)(dbp); 44116134Swpaul return(1); 44216134Swpaul } 44316134Swpaul 44416134Swpaul key.data = yp_last; 44516134Swpaul key.size = strlen(yp_last); 44616134Swpaul data.data = (char *)&yplastbuf; 44716134Swpaul data.size = strlen(yplastbuf); 44816134Swpaul 44916134Swpaul if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) { 45016134Swpaul yp_error("failed to update timestamp in %s/%s", domain, 45116134Swpaul maps[i]); 45216134Swpaul (void)(dbp->close)(dbp); 45316134Swpaul return(1); 45416134Swpaul } 45516134Swpaul 45616134Swpaul (void)(dbp->close)(dbp); 45716134Swpaul } 45816134Swpaul 45916134Swpaul return(0); 46016134Swpaul} 46116134Swpaul 46217431Swpaulstatic char *yp_mktmpnam() 46317431Swpaul{ 46417431Swpaul static char path[MAXPATHLEN]; 46517431Swpaul char *p; 46616134Swpaul 46717431Swpaul sprintf(path,"%s",passfile); 46817431Swpaul if ((p = strrchr(path, '/'))) 46917431Swpaul ++p; 47017431Swpaul else 47117431Swpaul p = path; 47217431Swpaul strcpy(p, "yppwtmp.XXXXXX"); 47317431Swpaul return(mktemp(path)); 47417431Swpaul} 47517431Swpaul 47614062Swpaulint * 47714062Swpaulyppasswdproc_update_1_svc(yppasswd *argp, struct svc_req *rqstp) 47814062Swpaul{ 47914062Swpaul static int result; 48014062Swpaul struct sockaddr_in *rqhost; 48114062Swpaul DBT key, data; 48214062Swpaul int rval = 0; 48314062Swpaul int pfd, tfd; 48414062Swpaul int pid; 48514062Swpaul int passwd_changed = 0; 48614062Swpaul int shell_changed = 0; 48714062Swpaul int gecos_changed = 0; 48814062Swpaul char *oldshell = NULL; 48914062Swpaul char *oldgecos = NULL; 49014062Swpaul char *passfile_hold; 49114062Swpaul char passfile_buf[MAXPATHLEN + 2]; 49214062Swpaul char *domain = yppasswd_domain; 49317431Swpaul static struct sockaddr_in clntaddr; 49417431Swpaul static struct timeval t_saved, t_test; 49514062Swpaul 49614062Swpaul /* 49714062Swpaul * Normal user updates always use the 'default' master.passwd file. 49814062Swpaul */ 49914062Swpaul 50014062Swpaul passfile = passfile_default; 50114062Swpaul result = 1; 50214062Swpaul 50314062Swpaul rqhost = svc_getcaller(rqstp->rq_xprt); 50414062Swpaul 50517431Swpaul gettimeofday(&t_test, NULL); 50617431Swpaul if (!bcmp((char *)rqhost, (char *)&clntaddr, 50717431Swpaul sizeof(struct sockaddr_in)) && 50817431Swpaul t_test.tv_sec > t_saved.tv_sec && 50917431Swpaul t_test.tv_sec - t_saved.tv_sec < 300) { 51017431Swpaul 51117431Swpaul bzero((char *)&clntaddr, sizeof(struct sockaddr_in)); 51217431Swpaul bzero((char *)&t_saved, sizeof(struct timeval)); 51317431Swpaul return(NULL); 51417431Swpaul } 51517431Swpaul 51617431Swpaul bcopy((char *)rqhost, (char *)&clntaddr, sizeof(struct sockaddr_in)); 51717431Swpaul gettimeofday(&t_saved, NULL); 51817431Swpaul 51914241Swpaul if (yp_access(resvport ? "master.passwd.byname" : NULL, rqstp)) { 52014241Swpaul yp_error("rejected update request from unauthorized host"); 52114241Swpaul svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED); 52214241Swpaul return(&result); 52314241Swpaul } 52414241Swpaul 52514062Swpaul /* 52614062Swpaul * Step one: find the user. (It's kinda pointless to 52714062Swpaul * proceed if the user doesn't exist.) We look for the 52814062Swpaul * user in the master.passwd.byname database, _NOT_ by 52914062Swpaul * using getpwent() and friends! We can't use getpwent() 53014062Swpaul * since the NIS master server is not guaranteed to be 53114062Swpaul * configured as an NIS client. 53214062Swpaul */ 53314062Swpaul 53414062Swpaul if (multidomain) { 53514062Swpaul if ((domain = find_domain(&argp->newpw)) == NULL) { 53614062Swpaul yp_error("multidomain lookup failed - aborting update"); 53714062Swpaul return(&result); 53814062Swpaul } else 53914062Swpaul yp_error("updating user %s in domain %s", 54014062Swpaul argp->newpw.pw_name, domain); 54114062Swpaul } 54214062Swpaul 54314062Swpaul key.data = argp->newpw.pw_name; 54414062Swpaul key.size = strlen(argp->newpw.pw_name); 54514062Swpaul 54614062Swpaul if ((rval=yp_get_record(domain,"master.passwd.byname", 54714062Swpaul &key, &data, 0)) != YP_TRUE) { 54814062Swpaul if (rval == YP_NOKEY) { 54914062Swpaul yp_error("user %s not found in passwd database", 55014062Swpaul argp->newpw.pw_name); 55114062Swpaul } else { 55214062Swpaul yp_error("database access error: %s", 55314062Swpaul yperr_string(rval)); 55414062Swpaul } 55514062Swpaul return(&result); 55614062Swpaul } 55714062Swpaul 55814062Swpaul /* Nul terminate, please. */ 55914062Swpaul *(char *)(data.data + data.size) = '\0'; 56014062Swpaul 56114062Swpaul copy_yp_pass(data.data, 1, data.size); 56214062Swpaul 56314062Swpaul /* Step 2: check that the supplied oldpass is valid. */ 56414062Swpaul 56514062Swpaul if (strcmp(crypt(argp->oldpass, yp_password.pw_passwd), 56614062Swpaul yp_password.pw_passwd)) { 56714062Swpaul yp_error("rejected change attempt -- bad password"); 56814062Swpaul yp_error("client address: %s username: %s", 56914062Swpaul inet_ntoa(rqhost->sin_addr), 57014062Swpaul argp->newpw.pw_name); 57114062Swpaul return(&result); 57214062Swpaul } 57314062Swpaul 57414062Swpaul /* Step 3: validate the arguments passed to us by the client. */ 57514062Swpaul 57614062Swpaul if (validate(&yp_password, &argp->newpw)) { 57714062Swpaul yp_error("rejecting change attempt: bad arguments"); 57814062Swpaul yp_error("client address: %s username: %s", 57914062Swpaul inet_ntoa(rqhost->sin_addr), 58014062Swpaul argp->newpw.pw_name); 58114062Swpaul svcerr_decode(rqstp->rq_xprt); 58214062Swpaul return(&result); 58314062Swpaul } 58414062Swpaul 58514062Swpaul /* Step 4: update the user's passwd structure. */ 58614062Swpaul 58714062Swpaul if (!no_chsh && strcmp(argp->newpw.pw_shell, yp_password.pw_shell)) { 58814062Swpaul oldshell = yp_password.pw_shell; 58914062Swpaul yp_password.pw_shell = argp->newpw.pw_shell; 59014062Swpaul shell_changed++; 59114062Swpaul } 59214062Swpaul 59314062Swpaul 59414062Swpaul if (!no_chfn && strcmp(argp->newpw.pw_gecos, yp_password.pw_gecos)) { 59514062Swpaul oldgecos = yp_password.pw_gecos; 59614062Swpaul yp_password.pw_gecos = argp->newpw.pw_gecos; 59714062Swpaul gecos_changed++; 59814062Swpaul } 59914062Swpaul 60014062Swpaul if (strcmp(argp->newpw.pw_passwd, yp_password.pw_passwd)) { 60114062Swpaul yp_password.pw_passwd = argp->newpw.pw_passwd; 60218047Swpaul yp_password.pw_change = 0; 60314062Swpaul passwd_changed++; 60414062Swpaul } 60514062Swpaul 60614062Swpaul /* 60714062Swpaul * If the caller specified a domain other than our 'default' 60814062Swpaul * domain, change the path to master.passwd accordingly. 60914062Swpaul */ 61014062Swpaul 61114062Swpaul if (strcmp(domain, yppasswd_domain)) { 61214062Swpaul snprintf(passfile_buf, sizeof(passfile_buf), 61316649Swpaul "%s/%s/master.passwd", yp_dir, domain); 61414062Swpaul passfile = (char *)&passfile_buf; 61514062Swpaul } 61614062Swpaul 61714062Swpaul /* Step 5: make a new password file with the updated info. */ 61814062Swpaul 61914062Swpaul if ((pfd = pw_lock()) < 0) { 62014062Swpaul return (&result); 62114062Swpaul } 62214062Swpaul if ((tfd = pw_tmp()) < 0) { 62314062Swpaul return (&result); 62414062Swpaul } 62514062Swpaul 62614062Swpaul if (pw_copy(pfd, tfd, &yp_password)) { 62714062Swpaul yp_error("failed to created updated password file -- \ 62814062Swpaulcleaning up and bailing out"); 62914062Swpaul unlink(tempname); 63014062Swpaul return(&result); 63114062Swpaul } 63214062Swpaul 63317431Swpaul passfile_hold = yp_mktmpnam(); 63414062Swpaul rename(passfile, passfile_hold); 63514062Swpaul if (strcmp(passfile, _PATH_MASTERPASSWD)) { 63614062Swpaul rename(tempname, passfile); 63714062Swpaul } else { 63816876Sguido if (pw_mkdb(argp->newpw.pw_name) < 0) { 63914062Swpaul yp_error("pwd_mkdb failed"); 64014062Swpaul return(&result); 64114062Swpaul } 64214062Swpaul } 64314062Swpaul 64416134Swpaul if (inplace) { 64516134Swpaul if ((rval = update_inplace(&yp_password, domain))) { 64616134Swpaul yp_error("inplace update failed -- rebuilding maps"); 64716134Swpaul } 64816134Swpaul } 64916134Swpaul 65014062Swpaul switch((pid = fork())) { 65114062Swpaul case 0: 65216134Swpaul if (inplace && !rval) { 65316134Swpaul execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 65479452Sbrian yppasswd_domain, "pushpw", (char *)NULL); 65516134Swpaul } else { 65616134Swpaul execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 65779452Sbrian yppasswd_domain, (char *)NULL); 65816134Swpaul } 65914062Swpaul yp_error("couldn't exec map update process: %s", 66014062Swpaul strerror(errno)); 66114062Swpaul unlink(passfile); 66214062Swpaul rename(passfile_hold, passfile); 66314062Swpaul exit(1); 66414062Swpaul break; 66514062Swpaul case -1: 66614062Swpaul yp_error("fork() failed: %s", strerror(errno)); 66714062Swpaul unlink(passfile); 66814062Swpaul rename(passfile_hold, passfile); 66917431Swpaul return(&result); 67014062Swpaul break; 67114062Swpaul default: 67217431Swpaul unlink(passfile_hold); 67314062Swpaul break; 67414062Swpaul } 67514062Swpaul 67614062Swpaul if (verbose) { 67714062Swpaul yp_error("update completed for user %s (uid %d):", 67814062Swpaul argp->newpw.pw_name, 67914062Swpaul argp->newpw.pw_uid); 68014062Swpaul 68114062Swpaul if (passwd_changed) 68214062Swpaul yp_error("password changed"); 68314062Swpaul 68414062Swpaul if (gecos_changed) 68514062Swpaul yp_error("gecos changed ('%s' -> '%s')", 68614062Swpaul oldgecos, argp->newpw.pw_gecos); 68714062Swpaul 68814062Swpaul if (shell_changed) 68914062Swpaul yp_error("shell changed ('%s' -> '%s')", 69014062Swpaul oldshell, argp->newpw.pw_shell); 69114062Swpaul } 69214062Swpaul 69314062Swpaul result = 0; 69414062Swpaul return (&result); 69514062Swpaul} 69614062Swpaul 69714062Swpaul/* 69814062Swpaul * Note that this function performs a little less sanity checking 69914062Swpaul * than the last one. Since only the superuser is allowed to use it, 70014062Swpaul * it is assumed that the caller knows what he's doing. 70114062Swpaul */ 70227758Swpaulint *yppasswdproc_update_master_1_svc(master_yppasswd *argp, 70327758Swpaul struct svc_req *rqstp) 70414062Swpaul{ 70527758Swpaul static int result; 70614062Swpaul int pfd, tfd; 70714062Swpaul int pid; 70814062Swpaul int rval = 0; 70914062Swpaul DBT key, data; 71014062Swpaul char *passfile_hold; 71114062Swpaul char passfile_buf[MAXPATHLEN + 2]; 71227758Swpaul struct sockaddr_in *rqhost; 71327758Swpaul struct cmessage *cm; 71427758Swpaul SVCXPRT *transp; 71514062Swpaul 71614062Swpaul result = 1; 71727758Swpaul 71827758Swpaul /* 71927758Swpaul * NO AF_INET CONNETCIONS ALLOWED! 72027758Swpaul */ 72127758Swpaul rqhost = svc_getcaller(rqstp->rq_xprt); 72227758Swpaul if (rqhost->sin_family != AF_UNIX) { 72327758Swpaul yp_error("Alert! %s/%d attempted to use superuser-only \ 72427758Swpaulprocedure!\n", inet_ntoa(rqhost->sin_addr), rqhost->sin_port); 72527758Swpaul svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED); 72627758Swpaul return(&result); 72727758Swpaul } 72827758Swpaul 72927758Swpaul transp = rqstp->rq_xprt; 73027758Swpaul 73127758Swpaul if (transp->xp_verf.oa_length < sizeof(struct cmessage) || 73227758Swpaul transp->xp_verf.oa_base == NULL || 73327758Swpaul transp->xp_verf.oa_flavor != AUTH_UNIX) { 73427758Swpaul yp_error("caller didn't send proper credentials"); 73527758Swpaul svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED); 73627758Swpaul return(&result); 73727758Swpaul } 73827758Swpaul 73927758Swpaul cm = (struct cmessage *)transp->xp_verf.oa_base; 74027758Swpaul if (cm->cmsg.cmsg_type != SCM_CREDS) { 74127758Swpaul yp_error("caller didn't send proper credentials"); 74227758Swpaul svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED); 74327758Swpaul return(&result); 74427758Swpaul } 74527758Swpaul 74627758Swpaul if (cm->cmcred.cmcred_euid) { 74727758Swpaul yp_error("caller euid is %d, expecting 0 -- rejecting request", 74827758Swpaul cm->cmcred.cmcred_euid); 74927758Swpaul svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED); 75027758Swpaul return(&result); 75127758Swpaul } 75227758Swpaul 75314062Swpaul passfile = passfile_default; 75414062Swpaul 75514062Swpaul key.data = argp->newpw.pw_name; 75614062Swpaul key.size = strlen(argp->newpw.pw_name); 75714062Swpaul 75814062Swpaul /* 75914062Swpaul * The superuser may add entries to the passwd maps if 76014062Swpaul * rpc.yppasswdd is started with the -a flag. Paranoia 76114062Swpaul * prevents me from allowing additions by default. 76214062Swpaul */ 76314062Swpaul if ((rval = yp_get_record(argp->domain, "master.passwd.byname", 76414062Swpaul &key, &data, 0)) != YP_TRUE) { 76514062Swpaul if (rval == YP_NOKEY) { 76614062Swpaul yp_error("user %s not found in passwd database", 76714062Swpaul argp->newpw.pw_name); 76814062Swpaul if (allow_additions) 76914062Swpaul yp_error("notice: adding user %s to \ 77014062Swpaulmaster.passwd database for domain %s", argp->newpw.pw_name, argp->domain); 77114062Swpaul else 77230377Scharnier yp_error("restart rpc.yppasswdd with the -a flag to \ 77330377Scharnierallow additions to be made to the password database"); 77414062Swpaul } else { 77514062Swpaul yp_error("database access error: %s", 77614062Swpaul yperr_string(rval)); 77714062Swpaul } 77814062Swpaul if (!allow_additions) 77927758Swpaul return(&result); 78014062Swpaul } else { 78114062Swpaul 78214062Swpaul /* Nul terminate, please. */ 78314062Swpaul *(char *)(data.data + data.size) = '\0'; 78414062Swpaul 78514062Swpaul copy_yp_pass(data.data, 1, data.size); 78614062Swpaul } 78714062Swpaul 78814062Swpaul /* 78914062Swpaul * Perform a small bit of sanity checking. 79014062Swpaul */ 79114062Swpaul if (validate_master(rval == YP_TRUE ? &yp_password:NULL,&argp->newpw)){ 79214062Swpaul yp_error("rejecting update attempt for %s: bad arguments", 79314062Swpaul argp->newpw.pw_name); 79427758Swpaul return(&result); 79514062Swpaul } 79614062Swpaul 79714062Swpaul /* 79814062Swpaul * If the caller specified a domain other than our 'default' 79914062Swpaul * domain, change the path to master.passwd accordingly. 80014062Swpaul */ 80114062Swpaul 80214062Swpaul if (strcmp(argp->domain, yppasswd_domain)) { 80314062Swpaul snprintf(passfile_buf, sizeof(passfile_buf), 80416649Swpaul "%s/%s/master.passwd", yp_dir, argp->domain); 80514062Swpaul passfile = (char *)&passfile_buf; 80614062Swpaul } 80714062Swpaul 80814062Swpaul if ((pfd = pw_lock()) < 0) { 80927758Swpaul return (&result); 81014062Swpaul } 81114062Swpaul if ((tfd = pw_tmp()) < 0) { 81227758Swpaul return (&result); 81314062Swpaul } 81414062Swpaul 81514062Swpaul if (pw_copy(pfd, tfd, (struct passwd *)&argp->newpw)) { 81614062Swpaul yp_error("failed to created updated password file -- \ 81714062Swpaulcleaning up and bailing out"); 81814062Swpaul unlink(tempname); 81927758Swpaul return(&result); 82014062Swpaul } 82114062Swpaul 82217431Swpaul passfile_hold = yp_mktmpnam(); 82314062Swpaul rename(passfile, passfile_hold); 82414062Swpaul if (strcmp(passfile, _PATH_MASTERPASSWD)) { 82514062Swpaul rename(tempname, passfile); 82614062Swpaul } else { 82716876Sguido if (pw_mkdb(argp->newpw.pw_name) < 0) { 82814062Swpaul yp_error("pwd_mkdb failed"); 82927758Swpaul return(&result); 83014062Swpaul } 83114062Swpaul } 83214062Swpaul 83316134Swpaul if (inplace) { 83416134Swpaul if ((rval = update_inplace((struct passwd *)&argp->newpw, 83516134Swpaul argp->domain))) { 83616134Swpaul yp_error("inplace update failed -- rebuilding maps"); 83716134Swpaul } 83816134Swpaul } 83916134Swpaul 84014062Swpaul switch((pid = fork())) { 84114062Swpaul case 0: 84216134Swpaul if (inplace && !rval) { 84316134Swpaul execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 84479452Sbrian argp->domain, "pushpw", (char *)NULL); 84516134Swpaul } else { 84616134Swpaul execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 84779452Sbrian argp->domain, (char *)NULL); 84816134Swpaul } 84914062Swpaul yp_error("couldn't exec map update process: %s", 85014062Swpaul strerror(errno)); 85114062Swpaul unlink(passfile); 85214062Swpaul rename(passfile_hold, passfile); 85314062Swpaul exit(1); 85414062Swpaul break; 85514062Swpaul case -1: 85614062Swpaul yp_error("fork() failed: %s", strerror(errno)); 85714062Swpaul unlink(passfile); 85814062Swpaul rename(passfile_hold, passfile); 85927758Swpaul return(&result); 86014062Swpaul break; 86114062Swpaul default: 86217431Swpaul unlink(passfile_hold); 86314062Swpaul break; 86414062Swpaul } 86514062Swpaul 86614062Swpaul yp_error("performed update of user %s (uid %d) domain %s", 86714062Swpaul argp->newpw.pw_name, 86814062Swpaul argp->newpw.pw_uid, 86914062Swpaul argp->domain); 87014062Swpaul 87114062Swpaul result = 0; 87227758Swpaul return(&result); 87314062Swpaul} 874