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