Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.sbin / rpc.yppasswdd / yppasswdd_server.c
1 /*
2  * Copyright (c) 1995, 1996
3  *      Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by Bill Paul.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33 #ifndef lint
34 static const char rcsid[] =
35   "$FreeBSD: src/usr.sbin/rpc.yppasswdd/yppasswdd_server.c,v 1.16.2.2 2002/02/15 00:46:57 des Exp $";
36 #endif /* not lint */
37
38 #include <stdio.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include <dirent.h>
44 #include <sys/stat.h>
45 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48 #include <limits.h>
49 #include <db.h>
50 #include <pwd.h>
51 #include <errno.h>
52 #include <signal.h>
53 #include <rpc/rpc.h>
54 #include <rpcsvc/yp.h>
55 #include <sys/types.h>
56 #include <sys/wait.h>
57 #include <sys/param.h>
58 #include <sys/fcntl.h>
59 struct dom_binding {};
60 #include <rpcsvc/ypclnt.h>
61 #include "yppasswdd_extern.h"
62 #include "yppasswd.h"
63 #include "yppasswd_private.h"
64
65 char *tempname;
66
67 void reaper(sig)
68         int sig;
69 {
70         extern pid_t pid;
71         extern int pstat;
72         int st;
73         int saved_errno;
74
75         saved_errno = errno;
76
77         if (sig > 0) {
78                 if (sig == SIGCHLD)
79                         while (wait3(&st, WNOHANG, NULL) > 0) ;
80         } else {
81                 pid = waitpid(pid, &pstat, 0);
82         }
83
84         errno = saved_errno;
85         return;
86 }
87
88 void install_reaper(on)
89         int on;
90 {
91         if (on) {
92                 signal(SIGCHLD, reaper);
93         } else {
94                 signal(SIGCHLD, SIG_DFL);
95         }
96         return;
97 }
98
99 static struct passwd yp_password;
100
101 static void copy_yp_pass(p, x, m)
102 char *p;
103 int x, m;
104 {
105         register char *t, *s = p;
106         static char *buf;
107
108         yp_password.pw_fields = 0;
109
110         buf = (char *)realloc(buf, m + 10);
111         bzero(buf, m + 10);
112
113         /* Turn all colons into NULLs */
114         while (strchr(s, ':')) {
115                 s = (strchr(s, ':') + 1);
116                 *(s - 1)= '\0';
117         }
118
119         t = buf;
120 #define EXPAND(e)       e = t; while ((*t++ = *p++));
121         EXPAND(yp_password.pw_name);
122         yp_password.pw_fields |= _PWF_NAME;
123         EXPAND(yp_password.pw_passwd);
124         yp_password.pw_fields |= _PWF_PASSWD;
125         yp_password.pw_uid = atoi(p);
126         p += (strlen(p) + 1);
127         yp_password.pw_fields |= _PWF_UID;
128         yp_password.pw_gid = atoi(p);
129         p += (strlen(p) + 1);
130         yp_password.pw_fields |= _PWF_GID;
131         if (x) {
132                 EXPAND(yp_password.pw_class);
133                 yp_password.pw_fields |= _PWF_CLASS;
134                 yp_password.pw_change = atol(p);
135                 p += (strlen(p) + 1);
136                 yp_password.pw_fields |= _PWF_CHANGE;
137                 yp_password.pw_expire = atol(p);
138                 p += (strlen(p) + 1);
139                 yp_password.pw_fields |= _PWF_EXPIRE;
140         }
141         EXPAND(yp_password.pw_gecos);
142         yp_password.pw_fields |= _PWF_GECOS;
143         EXPAND(yp_password.pw_dir);
144         yp_password.pw_fields |= _PWF_DIR;
145         EXPAND(yp_password.pw_shell);
146         yp_password.pw_fields |= _PWF_SHELL;
147
148         return;
149 }
150
151 static int validchars(arg)
152         char *arg;
153 {
154         int i;
155
156         for (i = 0; i < strlen(arg); i++) {
157                 if (iscntrl(arg[i])) {
158                         yp_error("string contains a control character");
159                         return(1);
160                 }
161                 if (arg[i] == ':') {
162                         yp_error("string contains a colon");
163                         return(1);
164                 }
165                 /* Be evil: truncate strings with \n in them silently. */
166                 if (arg[i] == '\n') {
167                         arg[i] = '\0';
168                         return(0);
169                 }
170         }
171         return(0);
172 }
173
174 static int validate_master(opw, npw)
175         struct passwd *opw;
176         struct x_master_passwd *npw;
177 {
178
179         if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
180                 yp_error("client tried to modify an NIS entry");
181                 return(1);
182         }
183
184         if (validchars(npw->pw_shell)) {
185                 yp_error("specified shell contains invalid characters");
186                 return(1);
187         }
188
189         if (validchars(npw->pw_gecos)) {
190                 yp_error("specified gecos field contains invalid characters");
191                 return(1);
192         }
193
194         if (validchars(npw->pw_passwd)) {
195                 yp_error("specified password contains invalid characters");
196                 return(1);
197         }
198         return(0);
199 }
200
201 static int validate(opw, npw)
202         struct passwd *opw;
203         struct x_passwd *npw;
204 {
205
206         if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
207                 yp_error("client tried to modify an NIS entry");
208                 return(1);
209         }
210
211         if (npw->pw_uid != opw->pw_uid) {
212                 yp_error("UID mismatch: client says user %s has UID %d",
213                          npw->pw_name, npw->pw_uid);
214                 yp_error("database says user %s has UID %d", opw->pw_name,
215                          opw->pw_uid);
216                 return(1);
217         }
218
219         if (npw->pw_gid != opw->pw_gid) {
220                 yp_error("GID mismatch: client says user %s has GID %d",
221                          npw->pw_name, npw->pw_gid);
222                 yp_error("database says user %s has GID %d", opw->pw_name,
223                          opw->pw_gid);
224                 return(1);
225         }
226
227         /*
228          * Don't allow the user to shoot himself in the foot,
229          * even on purpose.
230          */
231         if (!ok_shell(npw->pw_shell)) {
232                 yp_error("%s is not a valid shell", npw->pw_shell);
233                 return(1);
234         }
235
236         if (validchars(npw->pw_shell)) {
237                 yp_error("specified shell contains invalid characters");
238                 return(1);
239         }
240
241         if (validchars(npw->pw_gecos)) {
242                 yp_error("specified gecos field contains invalid characters");
243                 return(1);
244         }
245
246         if (validchars(npw->pw_passwd)) {
247                 yp_error("specified password contains invalid characters");
248                 return(1);
249         }
250         return(0);
251 }
252
253 /*
254  * Kludge alert:
255  * In order to have one rpc.yppasswdd support multiple domains,
256  * we have to cheat: we search each directory under /var/yp
257  * and try to match the user in each master.passwd.byname
258  * map that we find. If the user matches (username, uid and gid
259  * all agree), then we use that domain. If we match the user in
260  * more than one database, we must abort.
261  */
262 static char *find_domain(pw)
263         struct x_passwd *pw;
264 {
265         struct stat statbuf;
266         struct dirent *dirp;
267         DIR *dird;
268         char yp_mapdir[MAXPATHLEN + 2];
269         static char domain[YPMAXDOMAIN];
270         char *tmp = NULL;
271         DBT key, data;
272         int hit = 0;
273
274         yp_error("performing multidomain lookup");
275
276         if ((dird = opendir(yp_dir)) == NULL) {
277                 yp_error("opendir(%s) failed: %s", yp_dir, strerror(errno));
278                 return(NULL);
279         }
280
281         while ((dirp = readdir(dird)) != NULL) {
282                 snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s",
283                                                         yp_dir, dirp->d_name);
284                 if (stat(yp_mapdir, &statbuf) < 0) {
285                         yp_error("stat(%s) failed: %s", yp_mapdir,
286                                                         strerror(errno));
287                         closedir(dird);
288                         return(NULL);
289                 }
290                 if (S_ISDIR(statbuf.st_mode)) {
291                         tmp = (char *)dirp->d_name;
292                         key.data = pw->pw_name;
293                         key.size = strlen(pw->pw_name);
294
295                         if (yp_get_record(tmp,"master.passwd.byname",
296                                         &key, &data, 0) != YP_TRUE) {
297                                 continue;
298                         }
299                         *(char *)(data.data + data.size) = '\0';
300                         copy_yp_pass(data.data, 1, data.size);
301                         if (yp_password.pw_uid == pw->pw_uid &&
302                             yp_password.pw_gid == pw->pw_gid) {
303                                 hit++;
304                                 snprintf(domain, YPMAXDOMAIN, "%s", tmp);
305                         }
306                 }
307         }
308
309         closedir(dird);
310         if (hit > 1) {
311                 yp_error("found same user in two different domains");
312                 return(NULL);
313         } else
314                 return((char *)&domain);
315 }
316
317 static int update_inplace(pw, domain)
318         struct passwd *pw;
319         char *domain;
320 {
321         DB *dbp = NULL;
322         DBT key = { NULL, 0 };
323         DBT data = { NULL, 0 };
324         char pwbuf[YPMAXRECORD];
325         char keybuf[20];
326         int i;
327         char *maps[] = { "master.passwd.byname", "master.passwd.byuid",
328                          "passwd.byname", "passwd.byuid" };
329
330         char *formats[] = { "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
331                             "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
332                             "%s:%s:%d:%d:%s:%s:%s", "%s:%s:%d:%d:%s:%s:%s" };
333         char *ptr = NULL;
334         char *yp_last = "YP_LAST_MODIFIED";
335         char yplastbuf[YPMAXRECORD];
336
337         snprintf(yplastbuf, sizeof(yplastbuf), "%lu", time(NULL));
338
339         for (i = 0; i < 4; i++) {
340
341                 if (i % 2) {
342                         snprintf(keybuf, sizeof(keybuf), "%ld", pw->pw_uid);
343                         key.data = (char *)&keybuf;
344                         key.size = strlen(keybuf);
345                 } else {
346                         key.data = pw->pw_name;
347                         key.size = strlen(pw->pw_name);
348                 }
349
350                 /*
351                  * XXX The passwd.byname and passwd.byuid maps come in
352                  * two flavors: secure and insecure. The secure version
353                  * has a '*' in the password field whereas the insecure one
354                  * has a real crypted password. The maps will be insecure
355                  * if they were built with 'unsecure = TRUE' enabled in
356                  * /var/yp/Makefile, but we'd have no way of knowing if
357                  * this has been done unless we were to try parsing the
358                  * Makefile, which is a disgusting thought. Instead, we
359                  * read the records from the maps, skip to the first ':'
360                  * in them, and then look at the character immediately
361                  * following it. If it's an '*' then the map is 'secure'
362                  * and we must not insert a real password into the pw_passwd
363                  * field. If it's not an '*', then we put the real crypted
364                  * password in.
365                  */
366                 if (yp_get_record(domain,maps[i],&key,&data,1) != YP_TRUE) {
367                         yp_error("couldn't read %s/%s: %s", domain,
368                                                 maps[i], strerror(errno));
369                         return(1);
370                 }
371
372                 if ((ptr = strchr(data.data, ':')) == NULL) {
373                         yp_error("no colon in passwd record?!");
374                         return(1);
375                 }
376
377                 /*
378                  * XXX Supposing we have more than one user with the same
379                  * UID? (Or more than one user with the same name?) We could
380                  * end up modifying the wrong record if were not careful.
381                  */
382                 if (i % 2) {
383                         if (strncmp(data.data, pw->pw_name,
384                                                         strlen(pw->pw_name))) {
385                                 yp_error("warning: found entry for UID %d \
386 in map %s@%s with wrong name (%.*s)", pw->pw_uid, maps[i], domain,
387                                         ptr - (char *)data.data, data.data);
388                                 yp_error("there may be more than one user \
389 with the same UID - continuing");
390                                 continue;
391                         }
392                 } else {
393                         /*
394                          * We're really being ultra-paranoid here.
395                          * This is generally a 'can't happen' condition.
396                          */
397                         snprintf(pwbuf, sizeof(pwbuf), ":%d:%d:", pw->pw_uid,
398                                                                   pw->pw_gid);
399                         if (!strstr(data.data, pwbuf)) {
400                                 yp_error("warning: found entry for user %s \
401 in map %s@%s with wrong UID", pw->pw_name, maps[i], domain);
402                                 yp_error("there may be more than one user
403 with the same name - continuing");
404                                 continue;
405                         }
406                 }
407
408                 if (i < 2) {
409                         snprintf(pwbuf, sizeof(pwbuf), formats[i],
410                            pw->pw_name, pw->pw_passwd, pw->pw_uid,
411                            pw->pw_gid, pw->pw_class, pw->pw_change,
412                            pw->pw_expire, pw->pw_gecos, pw->pw_dir,
413                            pw->pw_shell);
414                 } else {
415                         snprintf(pwbuf, sizeof(pwbuf), formats[i],
416                            pw->pw_name, *(ptr+1) == '*' ? "*" : pw->pw_passwd,
417                            pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir,
418                            pw->pw_shell);
419                 }
420
421 #define FLAGS O_RDWR|O_CREAT
422
423                 if ((dbp = yp_open_db_rw(domain, maps[i], FLAGS)) == NULL) {
424                         yp_error("couldn't open %s/%s r/w: %s",domain,
425                                                 maps[i],strerror(errno));
426                         return(1);
427                 }
428
429                 data.data = pwbuf;
430                 data.size = strlen(pwbuf);
431
432                 if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
433                         yp_error("failed to update record in %s/%s", domain,
434                                                                 maps[i]);
435                         (void)(dbp->close)(dbp);
436                         return(1);
437                 }
438
439                 key.data = yp_last;
440                 key.size = strlen(yp_last);
441                 data.data = (char *)&yplastbuf;
442                 data.size = strlen(yplastbuf);
443
444                 if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
445                         yp_error("failed to update timestamp in %s/%s", domain,
446                                                                 maps[i]);
447                         (void)(dbp->close)(dbp);
448                         return(1);
449                 }
450
451                 (void)(dbp->close)(dbp);
452         }
453
454         return(0);
455 }
456
457 static char *yp_mktmpnam()
458 {
459         static char path[MAXPATHLEN];
460         char *p;
461
462         sprintf(path,"%s",passfile);
463         if ((p = strrchr(path, '/')))
464                 ++p;
465         else
466                 p = path;
467         strcpy(p, "yppwtmp.XXXXXX");
468         return(mktemp(path));
469 }
470
471 int *
472 yppasswdproc_update_1_svc(yppasswd *argp, struct svc_req *rqstp)
473 {
474         static int  result;
475         struct sockaddr_in *rqhost;
476         DBT key, data;
477         int rval = 0;
478         int pfd, tfd;
479         int pid;
480         int passwd_changed = 0;
481         int shell_changed = 0;
482         int gecos_changed = 0;
483         char *oldshell = NULL;
484         char *oldgecos = NULL;
485         char *passfile_hold;
486         char passfile_buf[MAXPATHLEN + 2];
487         char *domain = yppasswd_domain;
488         static struct sockaddr_in clntaddr;
489         static struct timeval t_saved, t_test;
490
491         /*
492          * Normal user updates always use the 'default' master.passwd file.
493          */
494
495         passfile = passfile_default;
496         result = 1;
497
498         rqhost = svc_getcaller(rqstp->rq_xprt);
499
500         gettimeofday(&t_test, NULL);
501         if (!bcmp((char *)rqhost, (char *)&clntaddr,
502                                                 sizeof(struct sockaddr_in)) &&
503                 t_test.tv_sec > t_saved.tv_sec &&
504                 t_test.tv_sec - t_saved.tv_sec < 300) {
505
506                 bzero((char *)&clntaddr, sizeof(struct sockaddr_in));
507                 bzero((char *)&t_saved, sizeof(struct timeval));
508                 return(NULL);
509         }
510
511         bcopy((char *)rqhost, (char *)&clntaddr, sizeof(struct sockaddr_in));
512         gettimeofday(&t_saved, NULL);
513
514         if (yp_access(resvport ? "master.passwd.byname" : NULL, rqstp)) {
515                 yp_error("rejected update request from unauthorized host");
516                 svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
517                 return(&result);
518         }
519
520         /*
521          * Step one: find the user. (It's kinda pointless to
522          * proceed if the user doesn't exist.) We look for the
523          * user in the master.passwd.byname database, _NOT_ by
524          * using getpwent() and friends! We can't use getpwent()
525          * since the NIS master server is not guaranteed to be
526          * configured as an NIS client.
527          */
528
529         if (multidomain) {
530                 if ((domain = find_domain(&argp->newpw)) == NULL) {
531                         yp_error("multidomain lookup failed - aborting update");
532                         return(&result);
533                 } else
534                         yp_error("updating user %s in domain %s",
535                                         argp->newpw.pw_name, domain);
536         }
537
538         key.data = argp->newpw.pw_name;
539         key.size = strlen(argp->newpw.pw_name);
540
541         if ((rval = yp_get_record(domain,"master.passwd.byname",
542                         &key, &data, 0)) != YP_TRUE) {
543                 if (rval == YP_NOKEY) {
544                         yp_error("user %s not found in passwd database",
545                                 argp->newpw.pw_name);
546                 } else {
547                         yp_error("database access error: %s",
548                                 yperr_string(rval));
549                 }
550                 return(&result);
551         }
552
553         /* Nul terminate, please. */
554         *(char *)(data.data + data.size) = '\0';
555
556         copy_yp_pass(data.data, 1, data.size);
557
558         /* Step 2: check that the supplied oldpass is valid. */
559
560         if (strcmp(crypt(argp->oldpass, yp_password.pw_passwd),
561                                         yp_password.pw_passwd)) {
562                 yp_error("rejected change attempt -- bad password");
563                 yp_error("client address: %s username: %s",
564                           inet_ntoa(rqhost->sin_addr),
565                           argp->newpw.pw_name);
566                 return(&result);
567         }
568
569         /* Step 3: validate the arguments passed to us by the client. */
570
571         if (validate(&yp_password, &argp->newpw)) {
572                 yp_error("rejecting change attempt: bad arguments");
573                 yp_error("client address: %s username: %s",
574                          inet_ntoa(rqhost->sin_addr),
575                          argp->newpw.pw_name);
576                 svcerr_decode(rqstp->rq_xprt);
577                 return(&result);
578         }
579
580         /* Step 4: update the user's passwd structure. */
581
582         if (!no_chsh && strcmp(argp->newpw.pw_shell, yp_password.pw_shell)) {
583                 oldshell = yp_password.pw_shell;
584                 yp_password.pw_shell = argp->newpw.pw_shell;
585                 shell_changed++;
586         }
587
588
589         if (!no_chfn && strcmp(argp->newpw.pw_gecos, yp_password.pw_gecos)) {
590                 oldgecos = yp_password.pw_gecos;
591                 yp_password.pw_gecos = argp->newpw.pw_gecos;
592                 gecos_changed++;
593         }
594
595         if (strcmp(argp->newpw.pw_passwd, yp_password.pw_passwd)) {
596                 yp_password.pw_passwd = argp->newpw.pw_passwd;
597                 yp_password.pw_change = 0;
598                 passwd_changed++;
599         }
600
601         /*
602          * If the caller specified a domain other than our 'default'
603          * domain, change the path to master.passwd accordingly.
604          */
605
606         if (strcmp(domain, yppasswd_domain)) {
607                 snprintf(passfile_buf, sizeof(passfile_buf),
608                         "%s/%s/master.passwd", yp_dir, domain);
609                 passfile = (char *)&passfile_buf;
610         }
611
612         /* Step 5: make a new password file with the updated info. */
613
614         if ((pfd = pw_lock()) < 0) {
615                 return (&result);
616         }
617         if ((tfd = pw_tmp()) < 0) {
618                 return (&result);
619         }
620
621         if (pw_copy(pfd, tfd, &yp_password)) {
622                 yp_error("failed to created updated password file -- \
623 cleaning up and bailing out");
624                 unlink(tempname);
625                 return(&result);
626         }
627
628         passfile_hold = yp_mktmpnam();
629         rename(passfile, passfile_hold);
630         if (strcmp(passfile, _PATH_MASTERPASSWD)) {
631                 rename(tempname, passfile);
632         } else {
633                 if (pw_mkdb(argp->newpw.pw_name) < 0) {
634                         yp_error("pwd_mkdb failed");
635                         return(&result);
636                 }
637         }
638
639         if (inplace) {
640                 if ((rval = update_inplace(&yp_password, domain))) {
641                         yp_error("inplace update failed -- rebuilding maps");
642                 }
643         }
644
645         switch ((pid = fork())) {
646         case 0:
647                 if (inplace && !rval) {
648                         execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
649                                 yppasswd_domain, "pushpw", NULL);
650                 } else {
651                         execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
652                                 yppasswd_domain, NULL);
653                 }
654                 yp_error("couldn't exec map update process: %s",
655                                         strerror(errno));
656                 unlink(passfile);
657                 rename(passfile_hold, passfile);
658                 exit(1);
659                 break;
660         case -1:
661                 yp_error("fork() failed: %s", strerror(errno));
662                 unlink(passfile);
663                 rename(passfile_hold, passfile);
664                 return(&result);
665                 break;
666         default:
667                 unlink(passfile_hold);
668                 break;
669         }
670
671         if (verbose) {
672                 yp_error("update completed for user %s (uid %d):",
673                                                 argp->newpw.pw_name,
674                                                 argp->newpw.pw_uid);
675
676                 if (passwd_changed)
677                         yp_error("password changed");
678
679                 if (gecos_changed)
680                         yp_error("gecos changed ('%s' -> '%s')",
681                                         oldgecos, argp->newpw.pw_gecos);
682
683                 if (shell_changed)
684                         yp_error("shell changed ('%s' -> '%s')",
685                                         oldshell, argp->newpw.pw_shell);
686         }
687
688         result = 0;
689         return (&result);
690 }
691
692 struct cmessage {
693         struct cmsghdr          cmsg;
694         struct cmsgcred         cmcred;
695 };
696
697 /*
698  * Note that this function performs a little less sanity checking
699  * than the last one. Since only the superuser is allowed to use it,
700  * it is assumed that the caller knows what he's doing.
701  */
702 int *yppasswdproc_update_master_1_svc(master_yppasswd *argp,
703                                         struct svc_req *rqstp)
704 {
705         static int result;
706         int pfd, tfd;
707         int pid;
708         int rval = 0;
709         DBT key, data;
710         char *passfile_hold;
711         char passfile_buf[MAXPATHLEN + 2];
712         struct sockaddr_in *rqhost;
713         struct cmessage                 *cm;
714         SVCXPRT                         *transp;
715
716         result = 1;
717
718         /*
719          * NO AF_INET CONNETCIONS ALLOWED!
720          */
721         rqhost = svc_getcaller(rqstp->rq_xprt);
722         if (rqhost->sin_family != AF_UNIX) {
723                 yp_error("Alert! %s/%d attempted to use superuser-only \
724 procedure!\n", inet_ntoa(rqhost->sin_addr), rqhost->sin_port);
725                 svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
726                 return(&result);
727         }
728
729         transp = rqstp->rq_xprt;
730
731         if (transp->xp_verf.oa_length < sizeof(struct cmessage) ||
732                 transp->xp_verf.oa_base == NULL ||
733                 transp->xp_verf.oa_flavor != AUTH_UNIX) {
734                 yp_error("caller didn't send proper credentials");
735                 svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
736                 return(&result);
737         }
738
739         cm = (struct cmessage *)transp->xp_verf.oa_base;
740         if (cm->cmsg.cmsg_type != SCM_CREDS) {
741                 yp_error("caller didn't send proper credentials");
742                 svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
743                 return(&result);
744         }
745
746         if (cm->cmcred.cmcred_euid) {
747                 yp_error("caller euid is %d, expecting 0 -- rejecting request",
748                                 cm->cmcred.cmcred_euid);
749                 svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
750                 return(&result);
751         }
752
753         passfile = passfile_default;
754
755         key.data = argp->newpw.pw_name;
756         key.size = strlen(argp->newpw.pw_name);
757
758         /*
759          * The superuser may add entries to the passwd maps if
760          * rpc.yppasswdd is started with the -a flag. Paranoia
761          * prevents me from allowing additions by default.
762          */
763         if ((rval = yp_get_record(argp->domain, "master.passwd.byname",
764                           &key, &data, 0)) != YP_TRUE) {
765                 if (rval == YP_NOKEY) {
766                         yp_error("user %s not found in passwd database",
767                                  argp->newpw.pw_name);
768                         if (allow_additions)
769                                 yp_error("notice: adding user %s to \
770 master.passwd database for domain %s", argp->newpw.pw_name, argp->domain);
771                         else
772                                 yp_error("restart rpc.yppasswdd with the -a flag to \
773 allow additions to be made to the password database");
774                 } else {
775                         yp_error("database access error: %s",
776                                  yperr_string(rval));
777                 }
778                 if (!allow_additions)
779                         return(&result);
780         } else {
781
782                 /* Nul terminate, please. */
783                 *(char *)(data.data + data.size) = '\0';
784
785                 copy_yp_pass(data.data, 1, data.size);
786         }
787
788         /*
789          * Perform a small bit of sanity checking.
790          */
791         if (validate_master(rval == YP_TRUE ? &yp_password:NULL,&argp->newpw)){
792                 yp_error("rejecting update attempt for %s: bad arguments",
793                          argp->newpw.pw_name);
794                 return(&result);
795         }
796
797         /*
798          * If the caller specified a domain other than our 'default'
799          * domain, change the path to master.passwd accordingly.
800          */
801
802         if (strcmp(argp->domain, yppasswd_domain)) {
803                 snprintf(passfile_buf, sizeof(passfile_buf),
804                         "%s/%s/master.passwd", yp_dir, argp->domain);
805                 passfile = (char *)&passfile_buf;
806         }
807
808         if ((pfd = pw_lock()) < 0) {
809                 return (&result);
810         }
811         if ((tfd = pw_tmp()) < 0) {
812                 return (&result);
813         }
814
815         if (pw_copy(pfd, tfd, (struct passwd  *)&argp->newpw)) {
816                 yp_error("failed to created updated password file -- \
817 cleaning up and bailing out");
818                 unlink(tempname);
819                 return(&result);
820         }
821
822         passfile_hold = yp_mktmpnam();
823         rename(passfile, passfile_hold);
824         if (strcmp(passfile, _PATH_MASTERPASSWD)) {
825                 rename(tempname, passfile);
826         } else {
827                 if (pw_mkdb(argp->newpw.pw_name) < 0) {
828                         yp_error("pwd_mkdb failed");
829                         return(&result);
830                 }
831         }
832
833         if (inplace) {
834                 if ((rval = update_inplace((struct passwd *)&argp->newpw,
835                                                         argp->domain))) {
836                         yp_error("inplace update failed -- rebuilding maps");
837                 }
838         }
839
840         switch ((pid = fork())) {
841         case 0:
842                 if (inplace && !rval) {
843                         execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
844                                 argp->domain, "pushpw", NULL);
845                 } else {
846                         execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
847                                 argp->domain, NULL);
848                 }
849                 yp_error("couldn't exec map update process: %s",
850                                         strerror(errno));
851                 unlink(passfile);
852                 rename(passfile_hold, passfile);
853                 exit(1);
854                 break;
855         case -1:
856                 yp_error("fork() failed: %s", strerror(errno));
857                 unlink(passfile);
858                 rename(passfile_hold, passfile);
859                 return(&result);
860                 break;
861         default:
862                 unlink(passfile_hold);
863                 break;
864         }
865
866         yp_error("performed update of user %s (uid %d) domain %s",
867                                                 argp->newpw.pw_name,
868                                                 argp->newpw.pw_uid,
869                                                 argp->domain);
870
871         result = 0;
872         return(&result);
873 }