yppasswdd_server.c revision 96389
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 96389 2002-05-11 03:54:21Z alfred $"; 3630377Scharnier#endif /* not lint */ 3730377Scharnier 3896222Sdes#include <sys/param.h> 3996222Sdes#include <sys/fcntl.h> 4096222Sdes#include <sys/socket.h> 4196222Sdes#include <sys/stat.h> 4296222Sdes#include <sys/wait.h> 4396222Sdes 4496222Sdes#include <arpa/inet.h> 4596222Sdes#include <netinet/in.h> 4696222Sdes 4714062Swpaul#include <ctype.h> 4896222Sdes#include <db.h> 4914062Swpaul#include <dirent.h> 5096222Sdes#include <errno.h> 5114062Swpaul#include <limits.h> 5214062Swpaul#include <pwd.h> 5314062Swpaul#include <signal.h> 5496222Sdes#include <stdio.h> 5596222Sdes#include <stdlib.h> 5696222Sdes#include <string.h> 5796222Sdes#include <unistd.h> 5896222Sdes 5996222Sdes#include <libgen.h> 6096222Sdes#include <libutil.h> 6196222Sdes 6214062Swpaul#include <rpc/rpc.h> 6314062Swpaul#include <rpcsvc/yp.h> 6496222Sdesstruct dom_binding; 6514062Swpaul#include <rpcsvc/ypclnt.h> 6614062Swpaul#include "yppasswdd_extern.h" 6714062Swpaul#include "yppasswd.h" 6814062Swpaul#include "yppasswd_private.h" 6996222Sdes#include "ypxfr_extern.h" 7096222Sdes#include "yp_extern.h" 7114062Swpaul 7214062Swpaulstatic struct passwd yp_password; 7314062Swpaul 7490298Sdesstatic void 7590298Sdescopy_yp_pass(char *p, int x, int m) 7614062Swpaul{ 7796222Sdes char *t, *s = p; 7814062Swpaul static char *buf; 7914062Swpaul 8014062Swpaul yp_password.pw_fields = 0; 8114062Swpaul 8296222Sdes buf = realloc(buf, m + 10); 8314062Swpaul bzero(buf, m + 10); 8414062Swpaul 8514062Swpaul /* Turn all colons into NULLs */ 8614062Swpaul while (strchr(s, ':')) { 8714062Swpaul s = (strchr(s, ':') + 1); 8814062Swpaul *(s - 1)= '\0'; 8914062Swpaul } 9014062Swpaul 9114062Swpaul t = buf; 9214062Swpaul#define EXPAND(e) e = t; while ((*t++ = *p++)); 9314062Swpaul EXPAND(yp_password.pw_name); 9414062Swpaul yp_password.pw_fields |= _PWF_NAME; 9514062Swpaul EXPAND(yp_password.pw_passwd); 9614062Swpaul yp_password.pw_fields |= _PWF_PASSWD; 9714062Swpaul yp_password.pw_uid = atoi(p); 9814062Swpaul p += (strlen(p) + 1); 9914062Swpaul yp_password.pw_fields |= _PWF_UID; 10014062Swpaul yp_password.pw_gid = atoi(p); 10114062Swpaul p += (strlen(p) + 1); 10214062Swpaul yp_password.pw_fields |= _PWF_GID; 10314062Swpaul if (x) { 10414062Swpaul EXPAND(yp_password.pw_class); 10514062Swpaul yp_password.pw_fields |= _PWF_CLASS; 10614062Swpaul yp_password.pw_change = atol(p); 10714062Swpaul p += (strlen(p) + 1); 10814062Swpaul yp_password.pw_fields |= _PWF_CHANGE; 10914062Swpaul yp_password.pw_expire = atol(p); 11014062Swpaul p += (strlen(p) + 1); 11114062Swpaul yp_password.pw_fields |= _PWF_EXPIRE; 11214062Swpaul } 11314062Swpaul EXPAND(yp_password.pw_gecos); 11414062Swpaul yp_password.pw_fields |= _PWF_GECOS; 11514062Swpaul EXPAND(yp_password.pw_dir); 11614062Swpaul yp_password.pw_fields |= _PWF_DIR; 11714062Swpaul EXPAND(yp_password.pw_shell); 11814062Swpaul yp_password.pw_fields |= _PWF_SHELL; 11914062Swpaul 12014062Swpaul return; 12114062Swpaul} 12214062Swpaul 12390298Sdesstatic int 12490298Sdesvalidchars(char *arg) 12514062Swpaul{ 12696222Sdes size_t i; 12714062Swpaul 12814062Swpaul for (i = 0; i < strlen(arg); i++) { 12914062Swpaul if (iscntrl(arg[i])) { 13014062Swpaul yp_error("string contains a control character"); 13114062Swpaul return(1); 13214062Swpaul } 13314062Swpaul if (arg[i] == ':') { 13414062Swpaul yp_error("string contains a colon"); 13514062Swpaul return(1); 13614062Swpaul } 13714062Swpaul /* Be evil: truncate strings with \n in them silently. */ 13814062Swpaul if (arg[i] == '\n') { 13914062Swpaul arg[i] = '\0'; 14014062Swpaul return(0); 14114062Swpaul } 14214062Swpaul } 14314062Swpaul return(0); 14414062Swpaul} 14514062Swpaul 14690298Sdesstatic int 14796222Sdesvalidate_master(struct passwd *opw __unused, struct x_master_passwd *npw) 14814062Swpaul{ 14914062Swpaul 15014062Swpaul if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') { 15114062Swpaul yp_error("client tried to modify an NIS entry"); 15214062Swpaul return(1); 15314062Swpaul } 15414062Swpaul 15514062Swpaul if (validchars(npw->pw_shell)) { 15614062Swpaul yp_error("specified shell contains invalid characters"); 15714062Swpaul return(1); 15814062Swpaul } 15914062Swpaul 16014062Swpaul if (validchars(npw->pw_gecos)) { 16114062Swpaul yp_error("specified gecos field contains invalid characters"); 16214062Swpaul return(1); 16314062Swpaul } 16414062Swpaul 16514062Swpaul if (validchars(npw->pw_passwd)) { 16614062Swpaul yp_error("specified password contains invalid characters"); 16714062Swpaul return(1); 16814062Swpaul } 16914062Swpaul return(0); 17014062Swpaul} 17114062Swpaul 17290298Sdesstatic int 17390298Sdesvalidate(struct passwd *opw, struct x_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 18196222Sdes if ((uid_t)npw->pw_uid != opw->pw_uid) { 18214062Swpaul yp_error("UID mismatch: client says user %s has UID %d", 18314062Swpaul npw->pw_name, npw->pw_uid); 18414062Swpaul yp_error("database says user %s has UID %d", opw->pw_name, 18514062Swpaul opw->pw_uid); 18614062Swpaul return(1); 18714062Swpaul } 18814062Swpaul 18996222Sdes if ((gid_t)npw->pw_gid != opw->pw_gid) { 19014062Swpaul yp_error("GID mismatch: client says user %s has GID %d", 19114062Swpaul npw->pw_name, npw->pw_gid); 19214062Swpaul yp_error("database says user %s has GID %d", opw->pw_name, 19314062Swpaul opw->pw_gid); 19414062Swpaul return(1); 19514062Swpaul } 19614062Swpaul 19714062Swpaul /* 19814062Swpaul * Don't allow the user to shoot himself in the foot, 19914062Swpaul * even on purpose. 20014062Swpaul */ 20114062Swpaul if (!ok_shell(npw->pw_shell)) { 20214062Swpaul yp_error("%s is not a valid shell", npw->pw_shell); 20314062Swpaul return(1); 20414062Swpaul } 20514062Swpaul 20614062Swpaul if (validchars(npw->pw_shell)) { 20714062Swpaul yp_error("specified shell contains invalid characters"); 20814062Swpaul return(1); 20914062Swpaul } 21014062Swpaul 21114062Swpaul if (validchars(npw->pw_gecos)) { 21214062Swpaul yp_error("specified gecos field contains invalid characters"); 21314062Swpaul return(1); 21414062Swpaul } 21514062Swpaul 21614062Swpaul if (validchars(npw->pw_passwd)) { 21714062Swpaul yp_error("specified password contains invalid characters"); 21814062Swpaul return(1); 21914062Swpaul } 22014062Swpaul return(0); 22114062Swpaul} 22214062Swpaul 22314062Swpaul/* 22414062Swpaul * Kludge alert: 22514062Swpaul * In order to have one rpc.yppasswdd support multiple domains, 22614062Swpaul * we have to cheat: we search each directory under /var/yp 22714062Swpaul * and try to match the user in each master.passwd.byname 22814062Swpaul * map that we find. If the user matches (username, uid and gid 22914062Swpaul * all agree), then we use that domain. If we match the user in 23014062Swpaul * more than one database, we must abort. 23114062Swpaul */ 23290298Sdesstatic char * 23390298Sdesfind_domain(struct x_passwd *pw) 23414062Swpaul{ 23514062Swpaul struct stat statbuf; 23614062Swpaul struct dirent *dirp; 23714062Swpaul DIR *dird; 23814062Swpaul char yp_mapdir[MAXPATHLEN + 2]; 23915687Swpaul static char domain[YPMAXDOMAIN]; 24014062Swpaul char *tmp = NULL; 24114062Swpaul DBT key, data; 24214062Swpaul int hit = 0; 24314062Swpaul 24414062Swpaul yp_error("performing multidomain lookup"); 24514062Swpaul 24614062Swpaul if ((dird = opendir(yp_dir)) == NULL) { 24714062Swpaul yp_error("opendir(%s) failed: %s", yp_dir, strerror(errno)); 24814062Swpaul return(NULL); 24914062Swpaul } 25014062Swpaul 25114062Swpaul while ((dirp = readdir(dird)) != NULL) { 25296222Sdes snprintf(yp_mapdir, sizeof yp_mapdir, "%s/%s", 25314062Swpaul yp_dir, dirp->d_name); 25414062Swpaul if (stat(yp_mapdir, &statbuf) < 0) { 25514062Swpaul yp_error("stat(%s) failed: %s", yp_mapdir, 25614062Swpaul strerror(errno)); 25714062Swpaul closedir(dird); 25814062Swpaul return(NULL); 25914062Swpaul } 26014062Swpaul if (S_ISDIR(statbuf.st_mode)) { 26114062Swpaul tmp = (char *)dirp->d_name; 26214062Swpaul key.data = pw->pw_name; 26314062Swpaul key.size = strlen(pw->pw_name); 26414062Swpaul 26514062Swpaul if (yp_get_record(tmp,"master.passwd.byname", 26614062Swpaul &key, &data, 0) != YP_TRUE) { 26714062Swpaul continue; 26814062Swpaul } 26996222Sdes *((char *)data.data + data.size) = '\0'; 27014062Swpaul copy_yp_pass(data.data, 1, data.size); 27196222Sdes if (yp_password.pw_uid == (uid_t)pw->pw_uid && 27296222Sdes yp_password.pw_gid == (gid_t)pw->pw_gid) { 27314062Swpaul hit++; 27415687Swpaul snprintf(domain, YPMAXDOMAIN, "%s", tmp); 27514062Swpaul } 27614062Swpaul } 27714062Swpaul } 27814062Swpaul 27914062Swpaul closedir(dird); 28014062Swpaul if (hit > 1) { 28114062Swpaul yp_error("found same user in two different domains"); 28214062Swpaul return(NULL); 28314062Swpaul } else 28416134Swpaul return((char *)&domain); 28514062Swpaul} 28614062Swpaul 28796222Sdesstatic const char *maps[] = { 28896222Sdes "master.passwd.byname", 28996222Sdes "master.passwd.byuid", 29096222Sdes "passwd.byname", 29196222Sdes "passwd.byuid" 29296222Sdes}; 29396222Sdes 29496222Sdesstatic const char *formats[] = { 29596222Sdes "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s", 29696222Sdes "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s", 29796222Sdes "%s:%s:%d:%d:%s:%s:%s", 29896222Sdes "%s:%s:%d:%d:%s:%s:%s" 29996222Sdes}; 30096222Sdes 30190298Sdesstatic int 30290298Sdesupdate_inplace(struct passwd *pw, char *domain) 30316134Swpaul{ 30416134Swpaul DB *dbp = NULL; 30516134Swpaul DBT key = { NULL, 0 }; 30616134Swpaul DBT data = { NULL, 0 }; 30716134Swpaul char pwbuf[YPMAXRECORD]; 30816134Swpaul char keybuf[20]; 30919778Speter int i; 31016134Swpaul char *ptr = NULL; 31196222Sdes static char yp_last[] = "YP_LAST_MODIFIED"; 31216134Swpaul char yplastbuf[YPMAXRECORD]; 31316134Swpaul 31496222Sdes snprintf(yplastbuf, sizeof yplastbuf, "%llu", 31596222Sdes (unsigned long long)time(NULL)); 31616134Swpaul 31716134Swpaul for (i = 0; i < 4; i++) { 31816134Swpaul 31916134Swpaul if (i % 2) { 32096222Sdes snprintf(keybuf, sizeof keybuf, 32196222Sdes "%llu", (unsigned long long)pw->pw_uid); 32296222Sdes key.data = &keybuf; 32316134Swpaul key.size = strlen(keybuf); 32416134Swpaul } else { 32516134Swpaul key.data = pw->pw_name; 32616134Swpaul key.size = strlen(pw->pw_name); 32716134Swpaul } 32816134Swpaul 32916134Swpaul /* 33016134Swpaul * XXX The passwd.byname and passwd.byuid maps come in 33116134Swpaul * two flavors: secure and insecure. The secure version 33216134Swpaul * has a '*' in the password field whereas the insecure one 33316134Swpaul * has a real crypted password. The maps will be insecure 33416134Swpaul * if they were built with 'unsecure = TRUE' enabled in 33516134Swpaul * /var/yp/Makefile, but we'd have no way of knowing if 33616134Swpaul * this has been done unless we were to try parsing the 33716134Swpaul * Makefile, which is a disgusting thought. Instead, we 33816134Swpaul * read the records from the maps, skip to the first ':' 33916134Swpaul * in them, and then look at the character immediately 34016134Swpaul * following it. If it's an '*' then the map is 'secure' 34116134Swpaul * and we must not insert a real password into the pw_passwd 34216134Swpaul * field. If it's not an '*', then we put the real crypted 34316134Swpaul * password in. 34416134Swpaul */ 34516134Swpaul if (yp_get_record(domain,maps[i],&key,&data,1) != YP_TRUE) { 34616134Swpaul yp_error("couldn't read %s/%s: %s", domain, 34716134Swpaul maps[i], strerror(errno)); 34816134Swpaul return(1); 34916134Swpaul } 35016134Swpaul 35116134Swpaul if ((ptr = strchr(data.data, ':')) == NULL) { 35216134Swpaul yp_error("no colon in passwd record?!"); 35316134Swpaul return(1); 35416134Swpaul } 35516134Swpaul 35619138Swpaul /* 35719138Swpaul * XXX Supposing we have more than one user with the same 35819138Swpaul * UID? (Or more than one user with the same name?) We could 35919138Swpaul * end up modifying the wrong record if were not careful. 36019138Swpaul */ 36119138Swpaul if (i % 2) { 36219138Swpaul if (strncmp(data.data, pw->pw_name, 36319138Swpaul strlen(pw->pw_name))) { 36419138Swpaul yp_error("warning: found entry for UID %d \ 36519138Swpaulin map %s@%s with wrong name (%.*s)", pw->pw_uid, maps[i], domain, 36696222Sdes ptr - (char *)data.data, (char *)data.data); 36719138Swpaul yp_error("there may be more than one user \ 36819138Swpaulwith the same UID - continuing"); 36919138Swpaul continue; 37019138Swpaul } 37119138Swpaul } else { 37219138Swpaul /* 37319138Swpaul * We're really being ultra-paranoid here. 37419138Swpaul * This is generally a 'can't happen' condition. 37519138Swpaul */ 37696222Sdes snprintf(pwbuf, sizeof pwbuf, ":%d:%d:", pw->pw_uid, 37719138Swpaul pw->pw_gid); 37819138Swpaul if (!strstr(data.data, pwbuf)) { 37919138Swpaul yp_error("warning: found entry for user %s \ 38019138Swpaulin map %s@%s with wrong UID", pw->pw_name, maps[i], domain); 38196389Salfred yp_error("there may be more than one user \ 38219138Swpaulwith the same name - continuing"); 38319138Swpaul continue; 38419138Swpaul } 38519138Swpaul } 38619138Swpaul 38716134Swpaul if (i < 2) { 38896222Sdes snprintf(pwbuf, sizeof pwbuf, formats[i], 38916134Swpaul pw->pw_name, pw->pw_passwd, pw->pw_uid, 39016134Swpaul pw->pw_gid, pw->pw_class, pw->pw_change, 39116134Swpaul pw->pw_expire, pw->pw_gecos, pw->pw_dir, 39216134Swpaul pw->pw_shell); 39316134Swpaul } else { 39496222Sdes snprintf(pwbuf, sizeof pwbuf, formats[i], 39516134Swpaul pw->pw_name, *(ptr+1) == '*' ? "*" : pw->pw_passwd, 39616134Swpaul pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir, 39716134Swpaul pw->pw_shell); 39816134Swpaul } 39916134Swpaul 40016134Swpaul#define FLAGS O_RDWR|O_CREAT 40116134Swpaul 40216134Swpaul if ((dbp = yp_open_db_rw(domain, maps[i], FLAGS)) == NULL) { 40316134Swpaul yp_error("couldn't open %s/%s r/w: %s",domain, 40416134Swpaul maps[i],strerror(errno)); 40516134Swpaul return(1); 40616134Swpaul } 40716134Swpaul 40816134Swpaul data.data = pwbuf; 40916134Swpaul data.size = strlen(pwbuf); 41016134Swpaul 41116134Swpaul if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) { 41216134Swpaul yp_error("failed to update record in %s/%s", domain, 41316134Swpaul maps[i]); 41416134Swpaul (void)(dbp->close)(dbp); 41516134Swpaul return(1); 41616134Swpaul } 41716134Swpaul 41816134Swpaul key.data = yp_last; 41916134Swpaul key.size = strlen(yp_last); 42016134Swpaul data.data = (char *)&yplastbuf; 42116134Swpaul data.size = strlen(yplastbuf); 42216134Swpaul 42316134Swpaul if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) { 42416134Swpaul yp_error("failed to update timestamp in %s/%s", domain, 42516134Swpaul maps[i]); 42616134Swpaul (void)(dbp->close)(dbp); 42716134Swpaul return(1); 42816134Swpaul } 42916134Swpaul 43016134Swpaul (void)(dbp->close)(dbp); 43116134Swpaul } 43216134Swpaul 43316134Swpaul return(0); 43416134Swpaul} 43516134Swpaul 43614062Swpaulint * 43714062Swpaulyppasswdproc_update_1_svc(yppasswd *argp, struct svc_req *rqstp) 43814062Swpaul{ 43914062Swpaul static int result; 44014062Swpaul struct sockaddr_in *rqhost; 44114062Swpaul DBT key, data; 44214062Swpaul int rval = 0; 44314062Swpaul int pfd, tfd; 44414062Swpaul int pid; 44514062Swpaul int passwd_changed = 0; 44614062Swpaul int shell_changed = 0; 44714062Swpaul int gecos_changed = 0; 44814062Swpaul char *oldshell = NULL; 44914062Swpaul char *oldgecos = NULL; 45014062Swpaul char *passfile_hold; 45114062Swpaul char passfile_buf[MAXPATHLEN + 2]; 45214062Swpaul char *domain = yppasswd_domain; 45317431Swpaul static struct sockaddr_in clntaddr; 45417431Swpaul static struct timeval t_saved, t_test; 45514062Swpaul 45614062Swpaul /* 45714062Swpaul * Normal user updates always use the 'default' master.passwd file. 45814062Swpaul */ 45914062Swpaul 46014062Swpaul passfile = passfile_default; 46114062Swpaul result = 1; 46214062Swpaul 46314062Swpaul rqhost = svc_getcaller(rqstp->rq_xprt); 46414062Swpaul 46517431Swpaul gettimeofday(&t_test, NULL); 46696222Sdes if (!bcmp(rqhost, &clntaddr, sizeof *rqhost) && 46717431Swpaul t_test.tv_sec > t_saved.tv_sec && 46817431Swpaul t_test.tv_sec - t_saved.tv_sec < 300) { 46917431Swpaul 47096222Sdes bzero(&clntaddr, sizeof clntaddr); 47196222Sdes bzero(&t_saved, sizeof t_saved); 47217431Swpaul return(NULL); 47317431Swpaul } 47417431Swpaul 47596222Sdes bcopy(rqhost, &clntaddr, sizeof clntaddr); 47617431Swpaul gettimeofday(&t_saved, NULL); 47717431Swpaul 47814241Swpaul if (yp_access(resvport ? "master.passwd.byname" : NULL, rqstp)) { 47914241Swpaul yp_error("rejected update request from unauthorized host"); 48014241Swpaul svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED); 48114241Swpaul return(&result); 48214241Swpaul } 48314241Swpaul 48414062Swpaul /* 48514062Swpaul * Step one: find the user. (It's kinda pointless to 48614062Swpaul * proceed if the user doesn't exist.) We look for the 48714062Swpaul * user in the master.passwd.byname database, _NOT_ by 48814062Swpaul * using getpwent() and friends! We can't use getpwent() 48914062Swpaul * since the NIS master server is not guaranteed to be 49014062Swpaul * configured as an NIS client. 49114062Swpaul */ 49214062Swpaul 49314062Swpaul if (multidomain) { 49414062Swpaul if ((domain = find_domain(&argp->newpw)) == NULL) { 49514062Swpaul yp_error("multidomain lookup failed - aborting update"); 49614062Swpaul return(&result); 49714062Swpaul } else 49814062Swpaul yp_error("updating user %s in domain %s", 49914062Swpaul argp->newpw.pw_name, domain); 50014062Swpaul } 50114062Swpaul 50214062Swpaul key.data = argp->newpw.pw_name; 50314062Swpaul key.size = strlen(argp->newpw.pw_name); 50414062Swpaul 50590297Sdes if ((rval = yp_get_record(domain,"master.passwd.byname", 50614062Swpaul &key, &data, 0)) != YP_TRUE) { 50714062Swpaul if (rval == YP_NOKEY) { 50814062Swpaul yp_error("user %s not found in passwd database", 50914062Swpaul argp->newpw.pw_name); 51014062Swpaul } else { 51114062Swpaul yp_error("database access error: %s", 51214062Swpaul yperr_string(rval)); 51314062Swpaul } 51414062Swpaul return(&result); 51514062Swpaul } 51614062Swpaul 51714062Swpaul /* Nul terminate, please. */ 51896222Sdes *((char *)data.data + data.size) = '\0'; 51914062Swpaul 52014062Swpaul copy_yp_pass(data.data, 1, data.size); 52114062Swpaul 52214062Swpaul /* Step 2: check that the supplied oldpass is valid. */ 52314062Swpaul 52414062Swpaul if (strcmp(crypt(argp->oldpass, yp_password.pw_passwd), 52514062Swpaul yp_password.pw_passwd)) { 52614062Swpaul yp_error("rejected change attempt -- bad password"); 52714062Swpaul yp_error("client address: %s username: %s", 52814062Swpaul inet_ntoa(rqhost->sin_addr), 52914062Swpaul argp->newpw.pw_name); 53014062Swpaul return(&result); 53114062Swpaul } 53214062Swpaul 53314062Swpaul /* Step 3: validate the arguments passed to us by the client. */ 53414062Swpaul 53514062Swpaul if (validate(&yp_password, &argp->newpw)) { 53614062Swpaul yp_error("rejecting change attempt: bad arguments"); 53714062Swpaul yp_error("client address: %s username: %s", 53814062Swpaul inet_ntoa(rqhost->sin_addr), 53914062Swpaul argp->newpw.pw_name); 54014062Swpaul svcerr_decode(rqstp->rq_xprt); 54114062Swpaul return(&result); 54214062Swpaul } 54314062Swpaul 54414062Swpaul /* Step 4: update the user's passwd structure. */ 54514062Swpaul 54614062Swpaul if (!no_chsh && strcmp(argp->newpw.pw_shell, yp_password.pw_shell)) { 54714062Swpaul oldshell = yp_password.pw_shell; 54814062Swpaul yp_password.pw_shell = argp->newpw.pw_shell; 54914062Swpaul shell_changed++; 55014062Swpaul } 55114062Swpaul 55214062Swpaul 55314062Swpaul if (!no_chfn && strcmp(argp->newpw.pw_gecos, yp_password.pw_gecos)) { 55414062Swpaul oldgecos = yp_password.pw_gecos; 55514062Swpaul yp_password.pw_gecos = argp->newpw.pw_gecos; 55614062Swpaul gecos_changed++; 55714062Swpaul } 55814062Swpaul 55914062Swpaul if (strcmp(argp->newpw.pw_passwd, yp_password.pw_passwd)) { 56014062Swpaul yp_password.pw_passwd = argp->newpw.pw_passwd; 56118047Swpaul yp_password.pw_change = 0; 56214062Swpaul passwd_changed++; 56314062Swpaul } 56414062Swpaul 56514062Swpaul /* 56614062Swpaul * If the caller specified a domain other than our 'default' 56714062Swpaul * domain, change the path to master.passwd accordingly. 56814062Swpaul */ 56914062Swpaul 57014062Swpaul if (strcmp(domain, yppasswd_domain)) { 57114062Swpaul snprintf(passfile_buf, sizeof(passfile_buf), 57216649Swpaul "%s/%s/master.passwd", yp_dir, domain); 57314062Swpaul passfile = (char *)&passfile_buf; 57414062Swpaul } 57514062Swpaul 57614062Swpaul /* Step 5: make a new password file with the updated info. */ 57714062Swpaul 57896222Sdes if (pw_init(dirname(passfile), passfile)) { 57996222Sdes yp_error("pw_init() failed"); 58096222Sdes return &result; 58114062Swpaul } 58296222Sdes if ((pfd = pw_lock()) == -1) { 58396222Sdes pw_fini(); 58496222Sdes yp_error("pw_lock() failed"); 58596222Sdes return &result; 58614062Swpaul } 58796222Sdes if ((tfd = pw_tmp(-1)) == -1) { 58896222Sdes pw_fini(); 58996222Sdes yp_error("pw_tmp() failed"); 59096222Sdes return &result; 59114062Swpaul } 59296222Sdes if (pw_copy(pfd, tfd, &yp_password, NULL) == -1) { 59396222Sdes pw_fini(); 59496222Sdes yp_error("pw_copy() failed"); 59596222Sdes return &result; 59614062Swpaul } 59796222Sdes if (pw_mkdb(yp_password.pw_name) == -1) { 59896222Sdes pw_fini(); 59996222Sdes yp_error("pw_mkdb() failed"); 60096222Sdes return &result; 60196222Sdes } 60296222Sdes pw_fini(); 60314062Swpaul 60416134Swpaul if (inplace) { 60516134Swpaul if ((rval = update_inplace(&yp_password, domain))) { 60616134Swpaul yp_error("inplace update failed -- rebuilding maps"); 60716134Swpaul } 60816134Swpaul } 60916134Swpaul 61090297Sdes switch ((pid = fork())) { 61114062Swpaul case 0: 61216134Swpaul if (inplace && !rval) { 61316134Swpaul execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 61479452Sbrian yppasswd_domain, "pushpw", (char *)NULL); 61516134Swpaul } else { 61616134Swpaul execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 61779452Sbrian yppasswd_domain, (char *)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 */ 66290298Sdesint * 66390298Sdesyppasswdproc_update_master_1_svc(master_yppasswd *argp, 66490298Sdes struct svc_req *rqstp) 66514062Swpaul{ 66627758Swpaul static int result; 66714062Swpaul int pfd, tfd; 66814062Swpaul int pid; 66990253Salfred uid_t uid; 67014062Swpaul int rval = 0; 67114062Swpaul DBT key, data; 67214062Swpaul char *passfile_hold; 67314062Swpaul char passfile_buf[MAXPATHLEN + 2]; 67427758Swpaul struct sockaddr_in *rqhost; 67590253Salfred SVCXPRT *transp; 67614062Swpaul 67714062Swpaul result = 1; 67890253Salfred transp = rqstp->rq_xprt; 67927758Swpaul 68027758Swpaul /* 68127758Swpaul * NO AF_INET CONNETCIONS ALLOWED! 68227758Swpaul */ 68390253Salfred rqhost = svc_getcaller(transp); 68427758Swpaul if (rqhost->sin_family != AF_UNIX) { 68527758Swpaul yp_error("Alert! %s/%d attempted to use superuser-only \ 68627758Swpaulprocedure!\n", inet_ntoa(rqhost->sin_addr), rqhost->sin_port); 68790253Salfred svcerr_auth(transp, AUTH_BADCRED); 68827758Swpaul return(&result); 68927758Swpaul } 69027758Swpaul 69190253Salfred if (rqstp->rq_cred.oa_flavor != AUTH_SYS) { 69227758Swpaul yp_error("caller didn't send proper credentials"); 69390253Salfred svcerr_auth(transp, AUTH_BADCRED); 69427758Swpaul return(&result); 69527758Swpaul } 69627758Swpaul 69790253Salfred if (__rpc_get_local_uid(transp, &uid) < 0) { 69827758Swpaul yp_error("caller didn't send proper credentials"); 69990253Salfred svcerr_auth(transp, AUTH_BADCRED); 70027758Swpaul return(&result); 70127758Swpaul } 70290297Sdes 70390253Salfred if (uid) { 70427758Swpaul yp_error("caller euid is %d, expecting 0 -- rejecting request", 70590253Salfred uid); 70627758Swpaul svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED); 70727758Swpaul return(&result); 70827758Swpaul } 70927758Swpaul 71014062Swpaul passfile = passfile_default; 71114062Swpaul 71214062Swpaul key.data = argp->newpw.pw_name; 71314062Swpaul key.size = strlen(argp->newpw.pw_name); 71414062Swpaul 71514062Swpaul /* 71614062Swpaul * The superuser may add entries to the passwd maps if 71714062Swpaul * rpc.yppasswdd is started with the -a flag. Paranoia 71814062Swpaul * prevents me from allowing additions by default. 71914062Swpaul */ 72014062Swpaul if ((rval = yp_get_record(argp->domain, "master.passwd.byname", 72114062Swpaul &key, &data, 0)) != YP_TRUE) { 72214062Swpaul if (rval == YP_NOKEY) { 72314062Swpaul yp_error("user %s not found in passwd database", 72414062Swpaul argp->newpw.pw_name); 72514062Swpaul if (allow_additions) 72614062Swpaul yp_error("notice: adding user %s to \ 72714062Swpaulmaster.passwd database for domain %s", argp->newpw.pw_name, argp->domain); 72814062Swpaul else 72930377Scharnier yp_error("restart rpc.yppasswdd with the -a flag to \ 73030377Scharnierallow additions to be made to the password database"); 73114062Swpaul } else { 73214062Swpaul yp_error("database access error: %s", 73314062Swpaul yperr_string(rval)); 73414062Swpaul } 73514062Swpaul if (!allow_additions) 73627758Swpaul return(&result); 73714062Swpaul } else { 73814062Swpaul 73914062Swpaul /* Nul terminate, please. */ 74096222Sdes *((char *)data.data + data.size) = '\0'; 74114062Swpaul 74214062Swpaul copy_yp_pass(data.data, 1, data.size); 74314062Swpaul } 74414062Swpaul 74514062Swpaul /* 74614062Swpaul * Perform a small bit of sanity checking. 74714062Swpaul */ 74814062Swpaul if (validate_master(rval == YP_TRUE ? &yp_password:NULL,&argp->newpw)){ 74914062Swpaul yp_error("rejecting update attempt for %s: bad arguments", 75014062Swpaul argp->newpw.pw_name); 75127758Swpaul return(&result); 75214062Swpaul } 75314062Swpaul 75414062Swpaul /* 75514062Swpaul * If the caller specified a domain other than our 'default' 75614062Swpaul * domain, change the path to master.passwd accordingly. 75714062Swpaul */ 75814062Swpaul 75914062Swpaul if (strcmp(argp->domain, yppasswd_domain)) { 76014062Swpaul snprintf(passfile_buf, sizeof(passfile_buf), 76116649Swpaul "%s/%s/master.passwd", yp_dir, argp->domain); 76214062Swpaul passfile = (char *)&passfile_buf; 76390297Sdes } 76414062Swpaul 76596222Sdes if (pw_init(dirname(passfile), passfile)) { 76696222Sdes yp_error("pw_init() failed"); 76796222Sdes return &result; 76814062Swpaul } 76996222Sdes if ((pfd = pw_lock()) == -1) { 77096222Sdes pw_fini(); 77196222Sdes yp_error("pw_lock() failed"); 77296222Sdes return &result; 77314062Swpaul } 77496222Sdes if ((tfd = pw_tmp(-1)) == -1) { 77596222Sdes pw_fini(); 77696222Sdes yp_error("pw_tmp() failed"); 77796222Sdes return &result; 77814062Swpaul } 77996222Sdes if (pw_copy(pfd, tfd, (struct passwd *)&argp->newpw, NULL) == -1) { 78096222Sdes pw_fini(); 78196222Sdes yp_error("pw_copy() failed"); 78296222Sdes return &result; 78314062Swpaul } 78496222Sdes if (pw_mkdb(argp->newpw.pw_name) == -1) { 78596222Sdes pw_fini(); 78696222Sdes yp_error("pw_mkdb() failed"); 78796222Sdes return &result; 78896222Sdes } 78996222Sdes pw_fini(); 79014062Swpaul 79116134Swpaul if (inplace) { 79216134Swpaul if ((rval = update_inplace((struct passwd *)&argp->newpw, 79316134Swpaul argp->domain))) { 79416134Swpaul yp_error("inplace update failed -- rebuilding maps"); 79516134Swpaul } 79616134Swpaul } 79716134Swpaul 79890297Sdes switch ((pid = fork())) { 79914062Swpaul case 0: 80016134Swpaul if (inplace && !rval) { 80116134Swpaul execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 80279452Sbrian argp->domain, "pushpw", (char *)NULL); 80316134Swpaul } else { 80416134Swpaul execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 80579452Sbrian argp->domain, (char *)NULL); 80616134Swpaul } 80714062Swpaul yp_error("couldn't exec map update process: %s", 80814062Swpaul strerror(errno)); 80914062Swpaul unlink(passfile); 81014062Swpaul rename(passfile_hold, passfile); 81114062Swpaul exit(1); 81214062Swpaul break; 81314062Swpaul case -1: 81414062Swpaul yp_error("fork() failed: %s", strerror(errno)); 81514062Swpaul unlink(passfile); 81614062Swpaul rename(passfile_hold, passfile); 81727758Swpaul return(&result); 81814062Swpaul break; 81914062Swpaul default: 82017431Swpaul unlink(passfile_hold); 82114062Swpaul break; 82214062Swpaul } 82314062Swpaul 82414062Swpaul yp_error("performed update of user %s (uid %d) domain %s", 82514062Swpaul argp->newpw.pw_name, 82614062Swpaul argp->newpw.pw_uid, 82714062Swpaul argp->domain); 82814062Swpaul 82914062Swpaul result = 0; 83027758Swpaul return(&result); 83114062Swpaul} 832