yppasswdd_server.c revision 16134
1253789Srpaulo/* 2253789Srpaulo * Copyright (c) 1995, 1996 3253789Srpaulo * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 4253789Srpaulo * 5253789Srpaulo * Redistribution and use in source and binary forms, with or without 6253789Srpaulo * modification, are permitted provided that the following conditions 7253789Srpaulo * are met: 8253789Srpaulo * 1. Redistributions of source code must retain the above copyright 9253789Srpaulo * notice, this list of conditions and the following disclaimer. 10253789Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 11253789Srpaulo * notice, this list of conditions and the following disclaimer in the 12253789Srpaulo * documentation and/or other materials provided with the distribution. 13253789Srpaulo * 3. All advertising materials mentioning features or use of this software 14253789Srpaulo * must display the following acknowledgement: 15253789Srpaulo * This product includes software developed by Bill Paul. 16253789Srpaulo * 4. Neither the name of the author nor the names of any co-contributors 17253789Srpaulo * may be used to endorse or promote products derived from this software 18253789Srpaulo * without specific prior written permission. 19253789Srpaulo * 20253789Srpaulo * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 21253789Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22253789Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23253789Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE 24253789Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25253789Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26253789Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27253789Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28253789Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29253789Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30253789Srpaulo * SUCH DAMAGE. 31253789Srpaulo * 32253789Srpaulo * $Id: yppasswdd_server.c,v 1.16 1996/06/04 00:00:19 wpaul Exp $ 33253789Srpaulo */ 34253789Srpaulo 35253789Srpaulo#include <stdio.h> 36253789Srpaulo#include <string.h> 37253789Srpaulo#include <ctype.h> 38253789Srpaulo#include <stdlib.h> 39253789Srpaulo#include <unistd.h> 40253789Srpaulo#include <dirent.h> 41253789Srpaulo#include <sys/stat.h> 42253789Srpaulo#include <sys/socket.h> 43253789Srpaulo#include <netinet/in.h> 44253789Srpaulo#include <arpa/inet.h> 45253789Srpaulo#include <limits.h> 46253789Srpaulo#include <db.h> 47253789Srpaulo#include <pwd.h> 48253789Srpaulo#include <errno.h> 49253789Srpaulo#include <signal.h> 50253789Srpaulo#include <rpc/rpc.h> 51253789Srpaulo#include <rpcsvc/yp.h> 52253789Srpaulo#include <sys/types.h> 53253789Srpaulo#include <sys/wait.h> 54253789Srpaulo#include <sys/param.h> 55253789Srpaulo#include <sys/fcntl.h> 56253789Srpaulostruct dom_binding {}; 57253789Srpaulo#include <rpcsvc/ypclnt.h> 58253789Srpaulo#include "yppasswdd_extern.h" 59253789Srpaulo#include "yppasswd.h" 60253789Srpaulo#include "yppasswd_private.h" 61253789Srpaulo#include "yppasswd_comm.h" 62253789Srpaulo 63253789Srpaulo#ifndef lint 64253789Srpaulostatic const char rcsid[] = "$Id: yppasswdd_server.c,v 1.16 1996/06/04 00:00:19 wpaul Exp $"; 65253789Srpaulo#endif /* not lint */ 66253789Srpaulo 67253789Srpaulochar *tempname; 68253789Srpaulo 69253789Srpaulovoid reaper(sig) 70253789Srpaulo int sig; 71253789Srpaulo{ 72253789Srpaulo extern pid_t pid; 73253789Srpaulo extern int pstat; 74253789Srpaulo int st; 75253789Srpaulo 76253789Srpaulo if (sig > 0) { 77253789Srpaulo if (sig == SIGCHLD) 78253789Srpaulo while(wait3(&st, WNOHANG, NULL) > 0) ; 79253789Srpaulo } else { 80253789Srpaulo pid = waitpid(pid, &pstat, 0); 81253789Srpaulo } 82253789Srpaulo return; 83253789Srpaulo} 84253789Srpaulo 85253789Srpaulovoid install_reaper(on) 86253789Srpaulo int on; 87253789Srpaulo{ 88253789Srpaulo if (on) { 89253789Srpaulo signal(SIGCHLD, reaper); 90253789Srpaulo } else { 91253789Srpaulo signal(SIGCHLD, SIG_DFL); 92253789Srpaulo } 93253789Srpaulo return; 94253789Srpaulo} 95253789Srpaulo 96253789Srpaulostatic struct passwd yp_password; 97253789Srpaulo 98253789Srpaulostatic void copy_yp_pass(p, x, m) 99253789Srpaulochar *p; 100253789Srpauloint x, m; 101253789Srpaulo{ 102253789Srpaulo register char *t, *s = p; 103253789Srpaulo static char *buf; 104253789Srpaulo 105253789Srpaulo yp_password.pw_fields = 0; 106253789Srpaulo 107253789Srpaulo buf = (char *)realloc(buf, m + 10); 108253789Srpaulo bzero(buf, m + 10); 109253789Srpaulo 110253789Srpaulo /* Turn all colons into NULLs */ 111253789Srpaulo while (strchr(s, ':')) { 112253789Srpaulo s = (strchr(s, ':') + 1); 113253789Srpaulo *(s - 1)= '\0'; 114253789Srpaulo } 115253789Srpaulo 116253789Srpaulo t = buf; 117253789Srpaulo#define EXPAND(e) e = t; while ((*t++ = *p++)); 118253789Srpaulo EXPAND(yp_password.pw_name); 119253789Srpaulo yp_password.pw_fields |= _PWF_NAME; 120253789Srpaulo EXPAND(yp_password.pw_passwd); 121253789Srpaulo yp_password.pw_fields |= _PWF_PASSWD; 122253789Srpaulo yp_password.pw_uid = atoi(p); 123253789Srpaulo p += (strlen(p) + 1); 124253789Srpaulo yp_password.pw_fields |= _PWF_UID; 125253789Srpaulo yp_password.pw_gid = atoi(p); 126253789Srpaulo p += (strlen(p) + 1); 127253789Srpaulo yp_password.pw_fields |= _PWF_GID; 128253789Srpaulo if (x) { 129253789Srpaulo EXPAND(yp_password.pw_class); 130253789Srpaulo yp_password.pw_fields |= _PWF_CLASS; 131253789Srpaulo yp_password.pw_change = atol(p); 132253789Srpaulo p += (strlen(p) + 1); 133253789Srpaulo yp_password.pw_fields |= _PWF_CHANGE; 134253789Srpaulo yp_password.pw_expire = atol(p); 135253789Srpaulo p += (strlen(p) + 1); 136253789Srpaulo yp_password.pw_fields |= _PWF_EXPIRE; 137253789Srpaulo } 138253789Srpaulo EXPAND(yp_password.pw_gecos); 139253789Srpaulo yp_password.pw_fields |= _PWF_GECOS; 140253789Srpaulo EXPAND(yp_password.pw_dir); 141253789Srpaulo yp_password.pw_fields |= _PWF_DIR; 142253789Srpaulo EXPAND(yp_password.pw_shell); 143253789Srpaulo yp_password.pw_fields |= _PWF_SHELL; 144253789Srpaulo 145253789Srpaulo return; 146253789Srpaulo} 147253789Srpaulo 148253789Srpaulostatic int validchars(arg) 149253789Srpaulo char *arg; 150253789Srpaulo{ 151253789Srpaulo int i; 152253789Srpaulo 153253789Srpaulo for (i = 0; i < strlen(arg); i++) { 154253789Srpaulo if (iscntrl(arg[i])) { 155253789Srpaulo yp_error("string contains a control character"); 156253789Srpaulo return(1); 157253789Srpaulo } 158253789Srpaulo if (arg[i] == ':') { 159253789Srpaulo yp_error("string contains a colon"); 160253789Srpaulo return(1); 161253789Srpaulo } 162253789Srpaulo /* Be evil: truncate strings with \n in them silently. */ 163253789Srpaulo if (arg[i] == '\n') { 164253789Srpaulo arg[i] = '\0'; 165253789Srpaulo return(0); 166253789Srpaulo } 167253789Srpaulo } 168253789Srpaulo return(0); 169253789Srpaulo} 170253789Srpaulo 171253789Srpaulostatic int validate_master(opw, npw) 172253789Srpaulo struct passwd *opw; 173253789Srpaulo struct x_master_passwd *npw; 174253789Srpaulo{ 175253789Srpaulo 176253789Srpaulo if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') { 177253789Srpaulo yp_error("client tried to modify an NIS entry"); 178253789Srpaulo return(1); 179253789Srpaulo } 180253789Srpaulo 181253789Srpaulo if (validchars(npw->pw_shell)) { 182253789Srpaulo yp_error("specified shell contains invalid characters"); 183253789Srpaulo return(1); 184253789Srpaulo } 185253789Srpaulo 186253789Srpaulo if (validchars(npw->pw_gecos)) { 187253789Srpaulo yp_error("specified gecos field contains invalid characters"); 188253789Srpaulo return(1); 189253789Srpaulo } 190253789Srpaulo 191253789Srpaulo if (validchars(npw->pw_passwd)) { 192253789Srpaulo yp_error("specified password contains invalid characters"); 193253789Srpaulo return(1); 194253789Srpaulo } 195253789Srpaulo return(0); 196253789Srpaulo} 197253789Srpaulo 198253789Srpaulostatic int validate(opw, npw) 199253789Srpaulo struct passwd *opw; 200253789Srpaulo struct x_passwd *npw; 201253789Srpaulo{ 202253789Srpaulo 203253789Srpaulo if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') { 204253789Srpaulo yp_error("client tried to modify an NIS entry"); 205253789Srpaulo return(1); 206253789Srpaulo } 207253789Srpaulo 208253789Srpaulo if (npw->pw_uid != opw->pw_uid) { 209253789Srpaulo yp_error("UID mismatch: client says user %s has UID %d", 210253789Srpaulo npw->pw_name, npw->pw_uid); 211253789Srpaulo yp_error("database says user %s has UID %d", opw->pw_name, 212253789Srpaulo opw->pw_uid); 213253789Srpaulo return(1); 214253789Srpaulo } 215253789Srpaulo 216253789Srpaulo if (npw->pw_gid != opw->pw_gid) { 217253789Srpaulo yp_error("GID mismatch: client says user %s has GID %d", 218253789Srpaulo npw->pw_name, npw->pw_gid); 219253789Srpaulo yp_error("database says user %s has GID %d", opw->pw_name, 220253789Srpaulo opw->pw_gid); 221253789Srpaulo return(1); 222253789Srpaulo } 223253789Srpaulo 224253789Srpaulo /* 225253789Srpaulo * Don't allow the user to shoot himself in the foot, 226253789Srpaulo * even on purpose. 227253789Srpaulo */ 228253789Srpaulo if (!ok_shell(npw->pw_shell)) { 229253789Srpaulo yp_error("%s is not a valid shell", npw->pw_shell); 230253789Srpaulo return(1); 231253789Srpaulo } 232253789Srpaulo 233253789Srpaulo if (validchars(npw->pw_shell)) { 234253789Srpaulo yp_error("specified shell contains invalid characters"); 235253789Srpaulo return(1); 236253789Srpaulo } 237253789Srpaulo 238253789Srpaulo if (validchars(npw->pw_gecos)) { 239253789Srpaulo yp_error("specified gecos field contains invalid characters"); 240253789Srpaulo return(1); 241253789Srpaulo } 242253789Srpaulo 243253789Srpaulo if (validchars(npw->pw_passwd)) { 244253789Srpaulo yp_error("specified password contains invalid characters"); 245253789Srpaulo return(1); 246253789Srpaulo } 247253789Srpaulo return(0); 248253789Srpaulo} 249253789Srpaulo 250253789Srpaulo/* 251253789Srpaulo * Kludge alert: 252253789Srpaulo * In order to have one rpc.yppasswdd support multiple domains, 253253789Srpaulo * we have to cheat: we search each directory under /var/yp 254253789Srpaulo * and try to match the user in each master.passwd.byname 255253789Srpaulo * map that we find. If the user matches (username, uid and gid 256253789Srpaulo * all agree), then we use that domain. If we match the user in 257253789Srpaulo * more than one database, we must abort. 258253789Srpaulo */ 259253789Srpaulostatic char *find_domain(pw) 260253789Srpaulo struct x_passwd *pw; 261253789Srpaulo{ 262253789Srpaulo struct stat statbuf; 263253789Srpaulo struct dirent *dirp; 264253789Srpaulo DIR *dird; 265253789Srpaulo char yp_mapdir[MAXPATHLEN + 2]; 266253789Srpaulo static char domain[YPMAXDOMAIN]; 267253789Srpaulo char *tmp = NULL; 268253789Srpaulo DBT key, data; 269253789Srpaulo int hit = 0; 270253789Srpaulo 271253789Srpaulo yp_error("performing multidomain lookup"); 272253789Srpaulo 273253789Srpaulo if ((dird = opendir(yp_dir)) == NULL) { 274253789Srpaulo yp_error("opendir(%s) failed: %s", yp_dir, strerror(errno)); 275253789Srpaulo return(NULL); 276253789Srpaulo } 277253789Srpaulo 278253789Srpaulo while ((dirp = readdir(dird)) != NULL) { 279253789Srpaulo snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s", 280253789Srpaulo yp_dir, dirp->d_name); 281253789Srpaulo if (stat(yp_mapdir, &statbuf) < 0) { 282253789Srpaulo yp_error("stat(%s) failed: %s", yp_mapdir, 283253789Srpaulo strerror(errno)); 284253789Srpaulo closedir(dird); 285253789Srpaulo return(NULL); 286253789Srpaulo } 287253789Srpaulo if (S_ISDIR(statbuf.st_mode)) { 288253789Srpaulo tmp = (char *)dirp->d_name; 289253789Srpaulo key.data = pw->pw_name; 290253789Srpaulo key.size = strlen(pw->pw_name); 291253789Srpaulo 292253789Srpaulo if (yp_get_record(tmp,"master.passwd.byname", 293253789Srpaulo &key, &data, 0) != YP_TRUE) { 294253789Srpaulo continue; 295253789Srpaulo } 296253789Srpaulo *(char *)(data.data + data.size) = '\0'; 297253789Srpaulo copy_yp_pass(data.data, 1, data.size); 298253789Srpaulo if (yp_password.pw_uid == pw->pw_uid && 299253789Srpaulo yp_password.pw_gid == pw->pw_gid) { 300253789Srpaulo hit++; 301253789Srpaulo snprintf(domain, YPMAXDOMAIN, "%s", tmp); 302253789Srpaulo } 303253789Srpaulo } 304253789Srpaulo } 305253789Srpaulo 306253789Srpaulo closedir(dird); 307253789Srpaulo if (hit > 1) { 308253789Srpaulo yp_error("found same user in two different domains"); 309253789Srpaulo return(NULL); 310253789Srpaulo } else 311253789Srpaulo return((char *)&domain); 312253789Srpaulo} 313253789Srpaulo 314253789Srpaulostatic int update_inplace(pw, domain) 315253789Srpaulo struct passwd *pw; 316253789Srpaulo char *domain; 317253789Srpaulo{ 318253789Srpaulo DB *dbp = NULL; 319253789Srpaulo DBT key = { NULL, 0 }; 320253789Srpaulo DBT data = { NULL, 0 }; 321253789Srpaulo char pwbuf[YPMAXRECORD]; 322253789Srpaulo char keybuf[20]; 323253789Srpaulo int rval, i; 324253789Srpaulo char *maps[] = { "master.passwd.byname", "master.passwd.byuid", 325253789Srpaulo "passwd.byname", "passwd.byuid" }; 326253789Srpaulo 327253789Srpaulo char *formats[] = { "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s", 328253789Srpaulo "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s", 329253789Srpaulo "%s:%s:%d:%d:%s:%s:%s", "%s:%s:%d:%d:%s:%s:%s" }; 330253789Srpaulo char *ptr = NULL; 331253789Srpaulo char *yp_last = "YP_LAST_MODIFIED"; 332253789Srpaulo char yplastbuf[YPMAXRECORD]; 333253789Srpaulo 334253789Srpaulo snprintf(yplastbuf, sizeof(yplastbuf), "%lu", time(NULL)); 335253789Srpaulo 336253789Srpaulo for (i = 0; i < 4; i++) { 337253789Srpaulo 338253789Srpaulo if (i % 2) { 339253789Srpaulo snprintf(keybuf, sizeof(keybuf), "%ld", pw->pw_uid); 340253789Srpaulo key.data = (char *)&keybuf; 341253789Srpaulo key.size = strlen(keybuf); 342253789Srpaulo } else { 343253789Srpaulo key.data = pw->pw_name; 344253789Srpaulo key.size = strlen(pw->pw_name); 345253789Srpaulo } 346253789Srpaulo 347253789Srpaulo /* 348253789Srpaulo * XXX The passwd.byname and passwd.byuid maps come in 349253789Srpaulo * two flavors: secure and insecure. The secure version 350253789Srpaulo * has a '*' in the password field whereas the insecure one 351253789Srpaulo * has a real crypted password. The maps will be insecure 352253789Srpaulo * if they were built with 'unsecure = TRUE' enabled in 353253789Srpaulo * /var/yp/Makefile, but we'd have no way of knowing if 354253789Srpaulo * this has been done unless we were to try parsing the 355253789Srpaulo * Makefile, which is a disgusting thought. Instead, we 356253789Srpaulo * read the records from the maps, skip to the first ':' 357253789Srpaulo * in them, and then look at the character immediately 358253789Srpaulo * following it. If it's an '*' then the map is 'secure' 359253789Srpaulo * and we must not insert a real password into the pw_passwd 360253789Srpaulo * field. If it's not an '*', then we put the real crypted 361253789Srpaulo * password in. 362253789Srpaulo */ 363253789Srpaulo if (yp_get_record(domain,maps[i],&key,&data,1) != YP_TRUE) { 364253789Srpaulo yp_error("couldn't read %s/%s: %s", domain, 365253789Srpaulo maps[i], strerror(errno)); 366253789Srpaulo return(1); 367253789Srpaulo } 368253789Srpaulo 369253789Srpaulo if ((ptr = strchr(data.data, ':')) == NULL) { 370253789Srpaulo yp_error("no colon in passwd record?!"); 371253789Srpaulo return(1); 372253789Srpaulo } 373253789Srpaulo 374253789Srpaulo if (i < 2) { 375253789Srpaulo snprintf(pwbuf, sizeof(pwbuf), formats[i], 376253789Srpaulo pw->pw_name, pw->pw_passwd, pw->pw_uid, 377253789Srpaulo pw->pw_gid, pw->pw_class, pw->pw_change, 378253789Srpaulo pw->pw_expire, pw->pw_gecos, pw->pw_dir, 379253789Srpaulo pw->pw_shell); 380253789Srpaulo } else { 381253789Srpaulo snprintf(pwbuf, sizeof(pwbuf), formats[i], 382253789Srpaulo pw->pw_name, *(ptr+1) == '*' ? "*" : pw->pw_passwd, 383253789Srpaulo pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir, 384253789Srpaulo pw->pw_shell); 385253789Srpaulo } 386253789Srpaulo 387253789Srpaulo#define FLAGS O_RDWR|O_CREAT 388253789Srpaulo 389253789Srpaulo if ((dbp = yp_open_db_rw(domain, maps[i], FLAGS)) == NULL) { 390253789Srpaulo yp_error("couldn't open %s/%s r/w: %s",domain, 391253789Srpaulo maps[i],strerror(errno)); 392253789Srpaulo return(1); 393253789Srpaulo } 394253789Srpaulo 395253789Srpaulo data.data = pwbuf; 396253789Srpaulo data.size = strlen(pwbuf); 397253789Srpaulo 398253789Srpaulo if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) { 399253789Srpaulo yp_error("failed to update record in %s/%s", domain, 400253789Srpaulo maps[i]); 401253789Srpaulo (void)(dbp->close)(dbp); 402253789Srpaulo return(1); 403253789Srpaulo } 404253789Srpaulo 405253789Srpaulo key.data = yp_last; 406253789Srpaulo key.size = strlen(yp_last); 407253789Srpaulo data.data = (char *)&yplastbuf; 408253789Srpaulo data.size = strlen(yplastbuf); 409253789Srpaulo 410253789Srpaulo if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) { 411253789Srpaulo yp_error("failed to update timestamp in %s/%s", domain, 412253789Srpaulo maps[i]); 413253789Srpaulo (void)(dbp->close)(dbp); 414253789Srpaulo return(1); 415253789Srpaulo } 416253789Srpaulo 417253789Srpaulo (void)(dbp->close)(dbp); 418253789Srpaulo } 419253789Srpaulo 420253789Srpaulo return(0); 421253789Srpaulo} 422253789Srpaulo 423253789Srpaulo 424253789Srpauloint * 425253789Srpauloyppasswdproc_update_1_svc(yppasswd *argp, struct svc_req *rqstp) 426253789Srpaulo{ 427253789Srpaulo static int result; 428253789Srpaulo struct sockaddr_in *rqhost; 429253789Srpaulo DBT key, data; 430253789Srpaulo int rval = 0; 431253789Srpaulo int pfd, tfd; 432253789Srpaulo int pid; 433253789Srpaulo int passwd_changed = 0; 434253789Srpaulo int shell_changed = 0; 435253789Srpaulo int gecos_changed = 0; 436253789Srpaulo char *oldshell = NULL; 437253789Srpaulo char *oldgecos = NULL; 438253789Srpaulo char *passfile_hold; 439253789Srpaulo char passfile_buf[MAXPATHLEN + 2]; 440253789Srpaulo char template[] = "/etc/yppwtmp.XXXXX"; 441253789Srpaulo char *domain = yppasswd_domain; 442253789Srpaulo 443253789Srpaulo /* 444253789Srpaulo * Normal user updates always use the 'default' master.passwd file. 445253789Srpaulo */ 446253789Srpaulo 447253789Srpaulo passfile = passfile_default; 448253789Srpaulo result = 1; 449253789Srpaulo 450253789Srpaulo rqhost = svc_getcaller(rqstp->rq_xprt); 451253789Srpaulo 452253789Srpaulo if (yp_access(resvport ? "master.passwd.byname" : NULL, rqstp)) { 453253789Srpaulo yp_error("rejected update request from unauthorized host"); 454253789Srpaulo svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED); 455253789Srpaulo return(&result); 456253789Srpaulo } 457253789Srpaulo 458253789Srpaulo /* 459253789Srpaulo * Step one: find the user. (It's kinda pointless to 460253789Srpaulo * proceed if the user doesn't exist.) We look for the 461253789Srpaulo * user in the master.passwd.byname database, _NOT_ by 462253789Srpaulo * using getpwent() and friends! We can't use getpwent() 463253789Srpaulo * since the NIS master server is not guaranteed to be 464253789Srpaulo * configured as an NIS client. 465253789Srpaulo */ 466253789Srpaulo 467253789Srpaulo if (multidomain) { 468253789Srpaulo if ((domain = find_domain(&argp->newpw)) == NULL) { 469253789Srpaulo yp_error("multidomain lookup failed - aborting update"); 470253789Srpaulo return(&result); 471253789Srpaulo } else 472253789Srpaulo yp_error("updating user %s in domain %s", 473253789Srpaulo argp->newpw.pw_name, domain); 474253789Srpaulo } 475253789Srpaulo 476253789Srpaulo key.data = argp->newpw.pw_name; 477253789Srpaulo key.size = strlen(argp->newpw.pw_name); 478253789Srpaulo 479253789Srpaulo if ((rval=yp_get_record(domain,"master.passwd.byname", 480253789Srpaulo &key, &data, 0)) != YP_TRUE) { 481253789Srpaulo if (rval == YP_NOKEY) { 482253789Srpaulo yp_error("user %s not found in passwd database", 483253789Srpaulo argp->newpw.pw_name); 484253789Srpaulo } else { 485253789Srpaulo yp_error("database access error: %s", 486253789Srpaulo yperr_string(rval)); 487253789Srpaulo } 488253789Srpaulo return(&result); 489253789Srpaulo } 490253789Srpaulo 491253789Srpaulo /* Nul terminate, please. */ 492253789Srpaulo *(char *)(data.data + data.size) = '\0'; 493253789Srpaulo 494253789Srpaulo copy_yp_pass(data.data, 1, data.size); 495253789Srpaulo 496253789Srpaulo /* Step 2: check that the supplied oldpass is valid. */ 497253789Srpaulo 498253789Srpaulo if (strcmp(crypt(argp->oldpass, yp_password.pw_passwd), 499253789Srpaulo yp_password.pw_passwd)) { 500253789Srpaulo yp_error("rejected change attempt -- bad password"); 501253789Srpaulo yp_error("client address: %s username: %s", 502253789Srpaulo inet_ntoa(rqhost->sin_addr), 503253789Srpaulo argp->newpw.pw_name); 504253789Srpaulo return(&result); 505253789Srpaulo } 506253789Srpaulo 507253789Srpaulo /* Step 3: validate the arguments passed to us by the client. */ 508253789Srpaulo 509253789Srpaulo if (validate(&yp_password, &argp->newpw)) { 510253789Srpaulo yp_error("rejecting change attempt: bad arguments"); 511253789Srpaulo yp_error("client address: %s username: %s", 512253789Srpaulo inet_ntoa(rqhost->sin_addr), 513253789Srpaulo argp->newpw.pw_name); 514253789Srpaulo svcerr_decode(rqstp->rq_xprt); 515253789Srpaulo return(&result); 516253789Srpaulo } 517253789Srpaulo 518266577Shselasky /* Step 4: update the user's passwd structure. */ 519253789Srpaulo 520253789Srpaulo if (!no_chsh && strcmp(argp->newpw.pw_shell, yp_password.pw_shell)) { 521253789Srpaulo oldshell = yp_password.pw_shell; 522253789Srpaulo yp_password.pw_shell = argp->newpw.pw_shell; 523253789Srpaulo shell_changed++; 524253789Srpaulo } 525253789Srpaulo 526253789Srpaulo 527253789Srpaulo if (!no_chfn && strcmp(argp->newpw.pw_gecos, yp_password.pw_gecos)) { 528253789Srpaulo oldgecos = yp_password.pw_gecos; 529253789Srpaulo yp_password.pw_gecos = argp->newpw.pw_gecos; 530266577Shselasky gecos_changed++; 531253789Srpaulo } 532253789Srpaulo 533253789Srpaulo if (strcmp(argp->newpw.pw_passwd, yp_password.pw_passwd)) { 534253789Srpaulo yp_password.pw_passwd = argp->newpw.pw_passwd; 535253789Srpaulo passwd_changed++; 536253789Srpaulo } 537253789Srpaulo 538253789Srpaulo /* 539253789Srpaulo * If the caller specified a domain other than our 'default' 540253789Srpaulo * domain, change the path to master.passwd accordingly. 541253789Srpaulo */ 542253789Srpaulo 543253789Srpaulo if (strcmp(domain, yppasswd_domain)) { 544253789Srpaulo snprintf(passfile_buf, sizeof(passfile_buf), 545253789Srpaulo "/var/yp/%s/master.passwd", domain); 546253789Srpaulo passfile = (char *)&passfile_buf; 547253789Srpaulo } 548253789Srpaulo 549253789Srpaulo /* Step 5: make a new password file with the updated info. */ 550253789Srpaulo 551253789Srpaulo if ((pfd = pw_lock()) < 0) { 552253789Srpaulo return (&result); 553253789Srpaulo } 554253789Srpaulo if ((tfd = pw_tmp()) < 0) { 555253789Srpaulo return (&result); 556253789Srpaulo } 557253789Srpaulo 558253789Srpaulo if (pw_copy(pfd, tfd, &yp_password)) { 559253789Srpaulo yp_error("failed to created updated password file -- \ 560253789Srpaulocleaning up and bailing out"); 561253789Srpaulo unlink(tempname); 562253789Srpaulo return(&result); 563253789Srpaulo } 564253789Srpaulo 565253789Srpaulo passfile_hold = mktemp((char *)&template); 566253789Srpaulo rename(passfile, passfile_hold); 567253789Srpaulo if (strcmp(passfile, _PATH_MASTERPASSWD)) { 568253789Srpaulo rename(tempname, passfile); 569253789Srpaulo } else { 570253789Srpaulo if (pw_mkdb() < 0) { 571253789Srpaulo yp_error("pwd_mkdb failed"); 572253789Srpaulo return(&result); 573253789Srpaulo } 574253789Srpaulo } 575253789Srpaulo 576253789Srpaulo if (inplace) { 577253789Srpaulo if ((rval = update_inplace(&yp_password, domain))) { 578253789Srpaulo yp_error("inplace update failed -- rebuilding maps"); 579253789Srpaulo } 580253789Srpaulo } 581253789Srpaulo 582253789Srpaulo switch((pid = fork())) { 583253789Srpaulo case 0: 584253789Srpaulo /* unlink(passfile_hold); */ 585253789Srpaulo if (inplace && !rval) { 586253789Srpaulo execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 587253789Srpaulo yppasswd_domain, "pushpw", NULL); 588253789Srpaulo } else { 589253789Srpaulo execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 590253789Srpaulo yppasswd_domain, NULL); 591253789Srpaulo } 592253789Srpaulo yp_error("couldn't exec map update process: %s", 593253789Srpaulo strerror(errno)); 594266577Shselasky unlink(passfile); 595253789Srpaulo rename(passfile_hold, passfile); 596253789Srpaulo exit(1); 597253789Srpaulo break; 598253789Srpaulo case -1: 599253789Srpaulo yp_error("fork() failed: %s", strerror(errno)); 600253789Srpaulo return(&result); 601253789Srpaulo unlink(passfile); 602253789Srpaulo rename(passfile_hold, passfile); 603253789Srpaulo break; 604253789Srpaulo default: 605253789Srpaulo break; 606253789Srpaulo } 607253789Srpaulo 608253789Srpaulo if (verbose) { 609253789Srpaulo yp_error("update completed for user %s (uid %d):", 610253789Srpaulo argp->newpw.pw_name, 611253789Srpaulo argp->newpw.pw_uid); 612253789Srpaulo 613253789Srpaulo if (passwd_changed) 614253789Srpaulo yp_error("password changed"); 615253789Srpaulo 616253789Srpaulo if (gecos_changed) 617253789Srpaulo yp_error("gecos changed ('%s' -> '%s')", 618253789Srpaulo oldgecos, argp->newpw.pw_gecos); 619253789Srpaulo 620253789Srpaulo if (shell_changed) 621253789Srpaulo yp_error("shell changed ('%s' -> '%s')", 622253789Srpaulo oldshell, argp->newpw.pw_shell); 623253789Srpaulo } 624253789Srpaulo 625253789Srpaulo result = 0; 626253789Srpaulo return (&result); 627253789Srpaulo 628253789Srpaulo} 629253789Srpaulo 630253789Srpaulo/* 631253789Srpaulo * Note that this function performs a little less sanity checking 632253789Srpaulo * than the last one. Since only the superuser is allowed to use it, 633253789Srpaulo * it is assumed that the caller knows what he's doing. 634253789Srpaulo */ 635253789Srpaulostatic int update_master(master_yppasswd *argp) 636253789Srpaulo{ 637253789Srpaulo int result; 638253789Srpaulo int pfd, tfd; 639253789Srpaulo int pid; 640253789Srpaulo int rval = 0; 641253789Srpaulo DBT key, data; 642253789Srpaulo char *passfile_hold; 643253789Srpaulo char passfile_buf[MAXPATHLEN + 2]; 644253789Srpaulo char template[] = "/etc/yppwtmp.XXXXX"; 645253789Srpaulo 646253789Srpaulo result = 1; 647253789Srpaulo passfile = passfile_default; 648253789Srpaulo 649253789Srpaulo key.data = argp->newpw.pw_name; 650253789Srpaulo key.size = strlen(argp->newpw.pw_name); 651253789Srpaulo 652253789Srpaulo /* 653253789Srpaulo * The superuser may add entries to the passwd maps if 654253789Srpaulo * rpc.yppasswdd is started with the -a flag. Paranoia 655253789Srpaulo * prevents me from allowing additions by default. 656253789Srpaulo */ 657253789Srpaulo if ((rval = yp_get_record(argp->domain, "master.passwd.byname", 658253789Srpaulo &key, &data, 0)) != YP_TRUE) { 659253789Srpaulo if (rval == YP_NOKEY) { 660253789Srpaulo yp_error("user %s not found in passwd database", 661253789Srpaulo argp->newpw.pw_name); 662253789Srpaulo if (allow_additions) 663253789Srpaulo yp_error("notice: adding user %s to \ 664253789Srpaulomaster.passwd database for domain %s", argp->newpw.pw_name, argp->domain); 665253789Srpaulo else 666253789Srpaulo yp_error("restart %s with the -a flag to \ 667253789Srpauloallow additions to be made to the password database", progname); 668253789Srpaulo } else { 669253789Srpaulo yp_error("database access error: %s", 670253789Srpaulo yperr_string(rval)); 671253789Srpaulo } 672253789Srpaulo if (!allow_additions) 673253789Srpaulo return(result); 674253789Srpaulo } else { 675253789Srpaulo 676253789Srpaulo /* Nul terminate, please. */ 677253789Srpaulo *(char *)(data.data + data.size) = '\0'; 678253789Srpaulo 679253789Srpaulo copy_yp_pass(data.data, 1, data.size); 680253789Srpaulo } 681253789Srpaulo 682253789Srpaulo /* 683253789Srpaulo * Perform a small bit of sanity checking. 684253789Srpaulo */ 685253789Srpaulo if (validate_master(rval == YP_TRUE ? &yp_password:NULL,&argp->newpw)){ 686253789Srpaulo yp_error("rejecting update attempt for %s: bad arguments", 687253789Srpaulo argp->newpw.pw_name); 688253789Srpaulo return(result); 689253789Srpaulo } 690253789Srpaulo 691253789Srpaulo /* 692253789Srpaulo * If the caller specified a domain other than our 'default' 693253789Srpaulo * domain, change the path to master.passwd accordingly. 694253789Srpaulo */ 695253789Srpaulo 696253789Srpaulo if (strcmp(argp->domain, yppasswd_domain)) { 697253789Srpaulo snprintf(passfile_buf, sizeof(passfile_buf), 698253789Srpaulo "/var/yp/%s/master.passwd", argp->domain); 699253789Srpaulo passfile = (char *)&passfile_buf; 700253789Srpaulo } 701267349Shselasky 702267349Shselasky if ((pfd = pw_lock()) < 0) { 703267349Shselasky return (result); 704253789Srpaulo } 705253789Srpaulo if ((tfd = pw_tmp()) < 0) { 706253789Srpaulo return (result); 707253789Srpaulo } 708253789Srpaulo 709253789Srpaulo if (pw_copy(pfd, tfd, (struct passwd *)&argp->newpw)) { 710253789Srpaulo yp_error("failed to created updated password file -- \ 711253789Srpaulocleaning up and bailing out"); 712253789Srpaulo unlink(tempname); 713253789Srpaulo return(result); 714253789Srpaulo } 715253789Srpaulo 716253789Srpaulo passfile_hold = mktemp((char *)&template); 717253789Srpaulo rename(passfile, passfile_hold); 718253789Srpaulo if (strcmp(passfile, _PATH_MASTERPASSWD)) { 719253789Srpaulo rename(tempname, passfile); 720253789Srpaulo } else { 721253789Srpaulo if (pw_mkdb() < 0) { 722253789Srpaulo yp_error("pwd_mkdb failed"); 723253789Srpaulo return(result); 724253789Srpaulo } 725253789Srpaulo } 726253789Srpaulo 727253789Srpaulo if (inplace) { 728253789Srpaulo if ((rval = update_inplace((struct passwd *)&argp->newpw, 729253789Srpaulo argp->domain))) { 730253789Srpaulo yp_error("inplace update failed -- rebuilding maps"); 731253789Srpaulo } 732253789Srpaulo } 733253789Srpaulo 734253789Srpaulo switch((pid = fork())) { 735253789Srpaulo case 0: 736253789Srpaulo close(yp_sock); 737253789Srpaulo if (inplace && !rval) { 738253789Srpaulo execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 739253789Srpaulo argp->domain, "pushpw", NULL); 740253789Srpaulo } else { 741253789Srpaulo execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile, 742253789Srpaulo argp->domain, NULL); 743253789Srpaulo } 744253789Srpaulo yp_error("couldn't exec map update process: %s", 745253789Srpaulo strerror(errno)); 746253789Srpaulo unlink(passfile); 747253789Srpaulo rename(passfile_hold, passfile); 748253789Srpaulo exit(1); 749253789Srpaulo break; 750253789Srpaulo case -1: 751253789Srpaulo yp_error("fork() failed: %s", strerror(errno)); 752253789Srpaulo unlink(passfile); 753267349Shselasky rename(passfile_hold, passfile); 754253789Srpaulo return(result); 755267349Shselasky break; 756253789Srpaulo default: 757253789Srpaulo break; 758253789Srpaulo } 759253789Srpaulo 760253789Srpaulo yp_error("performed update of user %s (uid %d) domain %s", 761253789Srpaulo argp->newpw.pw_name, 762253789Srpaulo argp->newpw.pw_uid, 763253789Srpaulo argp->domain); 764253789Srpaulo 765253789Srpaulo result = 0; 766253789Srpaulo return(result); 767253789Srpaulo} 768253789Srpaulo 769253789Srpaulo/* 770253789Srpaulo * Pseudo-dispatcher for private 'superuser-only' update handler. 771 */ 772void do_master() 773{ 774 struct master_yppasswd *pw; 775 776 if ((pw = getdat(yp_sock)) == NULL) { 777 return; 778 } 779 780 yp_error("received update request from superuser on localhost"); 781 sendresp(update_master(pw)); 782 783 /* Remember to free args. */ 784 xdr_free(xdr_master_yppasswd, (char *)pw); 785 786 return; 787} 788