]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/rpc.yppasswdd/yppasswdd_server.c
This commit was generated by cvs2svn to compensate for changes in r86629,
[FreeBSD/FreeBSD.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$";
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 struct cmessage {
66         struct cmsghdr cmsg;
67         struct cmsgcred cmcred;
68 };
69
70 char *tempname;
71
72 void reaper(sig)
73         int sig;
74 {
75         extern pid_t pid;
76         extern int pstat;
77         int st;
78         int saved_errno;
79
80         saved_errno = errno;
81
82         if (sig > 0) {
83                 if (sig == SIGCHLD)
84                         while(wait3(&st, WNOHANG, NULL) > 0) ;
85         } else {
86                 pid = waitpid(pid, &pstat, 0);
87         }
88
89         errno = saved_errno;
90         return;
91 }
92
93 void install_reaper(on)
94         int on;
95 {
96         if (on) {
97                 signal(SIGCHLD, reaper);
98         } else {
99                 signal(SIGCHLD, SIG_DFL);
100         }
101         return;
102 }
103
104 static struct passwd yp_password;
105
106 static void copy_yp_pass(p, x, m)
107 char *p;
108 int x, m;
109 {
110         register char *t, *s = p;
111         static char *buf;
112
113         yp_password.pw_fields = 0;
114
115         buf = (char *)realloc(buf, m + 10);
116         bzero(buf, m + 10);
117
118         /* Turn all colons into NULLs */
119         while (strchr(s, ':')) {
120                 s = (strchr(s, ':') + 1);
121                 *(s - 1)= '\0';
122         }
123
124         t = buf;
125 #define EXPAND(e)       e = t; while ((*t++ = *p++));
126         EXPAND(yp_password.pw_name);
127         yp_password.pw_fields |= _PWF_NAME;
128         EXPAND(yp_password.pw_passwd);
129         yp_password.pw_fields |= _PWF_PASSWD;
130         yp_password.pw_uid = atoi(p);
131         p += (strlen(p) + 1);
132         yp_password.pw_fields |= _PWF_UID;
133         yp_password.pw_gid = atoi(p);
134         p += (strlen(p) + 1);
135         yp_password.pw_fields |= _PWF_GID;
136         if (x) {
137                 EXPAND(yp_password.pw_class);
138                 yp_password.pw_fields |= _PWF_CLASS;
139                 yp_password.pw_change = atol(p);
140                 p += (strlen(p) + 1);
141                 yp_password.pw_fields |= _PWF_CHANGE;
142                 yp_password.pw_expire = atol(p);
143                 p += (strlen(p) + 1);
144                 yp_password.pw_fields |= _PWF_EXPIRE;
145         }
146         EXPAND(yp_password.pw_gecos);
147         yp_password.pw_fields |= _PWF_GECOS;
148         EXPAND(yp_password.pw_dir);
149         yp_password.pw_fields |= _PWF_DIR;
150         EXPAND(yp_password.pw_shell);
151         yp_password.pw_fields |= _PWF_SHELL;
152
153         return;
154 }
155
156 static int validchars(arg)
157         char *arg;
158 {
159         int i;
160
161         for (i = 0; i < strlen(arg); i++) {
162                 if (iscntrl(arg[i])) {
163                         yp_error("string contains a control character");
164                         return(1);
165                 }
166                 if (arg[i] == ':') {
167                         yp_error("string contains a colon");
168                         return(1);
169                 }
170                 /* Be evil: truncate strings with \n in them silently. */
171                 if (arg[i] == '\n') {
172                         arg[i] = '\0';
173                         return(0);
174                 }
175         }
176         return(0);
177 }
178
179 static int validate_master(opw, npw)
180         struct passwd *opw;
181         struct x_master_passwd *npw;
182 {
183
184         if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
185                 yp_error("client tried to modify an NIS entry");
186                 return(1);
187         }
188
189         if (validchars(npw->pw_shell)) {
190                 yp_error("specified shell contains invalid characters");
191                 return(1);
192         }
193
194         if (validchars(npw->pw_gecos)) {
195                 yp_error("specified gecos field contains invalid characters");
196                 return(1);
197         }
198
199         if (validchars(npw->pw_passwd)) {
200                 yp_error("specified password contains invalid characters");
201                 return(1);
202         }
203         return(0);
204 }
205
206 static int validate(opw, npw)
207         struct passwd *opw;
208         struct x_passwd *npw;
209 {
210
211         if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
212                 yp_error("client tried to modify an NIS entry");
213                 return(1);
214         }
215
216         if (npw->pw_uid != opw->pw_uid) {
217                 yp_error("UID mismatch: client says user %s has UID %d",
218                          npw->pw_name, npw->pw_uid);
219                 yp_error("database says user %s has UID %d", opw->pw_name,
220                          opw->pw_uid);
221                 return(1);
222         }
223
224         if (npw->pw_gid != opw->pw_gid) {
225                 yp_error("GID mismatch: client says user %s has GID %d",
226                          npw->pw_name, npw->pw_gid);
227                 yp_error("database says user %s has GID %d", opw->pw_name,
228                          opw->pw_gid);
229                 return(1);
230         }
231
232         /*
233          * Don't allow the user to shoot himself in the foot,
234          * even on purpose.
235          */
236         if (!ok_shell(npw->pw_shell)) {
237                 yp_error("%s is not a valid shell", npw->pw_shell);
238                 return(1);
239         }
240
241         if (validchars(npw->pw_shell)) {
242                 yp_error("specified shell contains invalid characters");
243                 return(1);
244         }
245
246         if (validchars(npw->pw_gecos)) {
247                 yp_error("specified gecos field contains invalid characters");
248                 return(1);
249         }
250
251         if (validchars(npw->pw_passwd)) {
252                 yp_error("specified password contains invalid characters");
253                 return(1);
254         }
255         return(0);
256 }
257
258 /*
259  * Kludge alert:
260  * In order to have one rpc.yppasswdd support multiple domains,
261  * we have to cheat: we search each directory under /var/yp
262  * and try to match the user in each master.passwd.byname
263  * map that we find. If the user matches (username, uid and gid
264  * all agree), then we use that domain. If we match the user in
265  * more than one database, we must abort.
266  */
267 static char *find_domain(pw)
268         struct x_passwd *pw;
269 {
270         struct stat statbuf;
271         struct dirent *dirp;
272         DIR *dird;
273         char yp_mapdir[MAXPATHLEN + 2];
274         static char domain[YPMAXDOMAIN];
275         char *tmp = NULL;
276         DBT key, data;
277         int hit = 0;
278
279         yp_error("performing multidomain lookup");
280
281         if ((dird = opendir(yp_dir)) == NULL) {
282                 yp_error("opendir(%s) failed: %s", yp_dir, strerror(errno));
283                 return(NULL);
284         }
285
286         while ((dirp = readdir(dird)) != NULL) {
287                 snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s",
288                                                         yp_dir, dirp->d_name);
289                 if (stat(yp_mapdir, &statbuf) < 0) {
290                         yp_error("stat(%s) failed: %s", yp_mapdir,
291                                                         strerror(errno));
292                         closedir(dird);
293                         return(NULL);
294                 }
295                 if (S_ISDIR(statbuf.st_mode)) {
296                         tmp = (char *)dirp->d_name;
297                         key.data = pw->pw_name;
298                         key.size = strlen(pw->pw_name);
299
300                         if (yp_get_record(tmp,"master.passwd.byname",
301                                         &key, &data, 0) != YP_TRUE) {
302                                 continue;
303                         }
304                         *(char *)(data.data + data.size) = '\0';
305                         copy_yp_pass(data.data, 1, data.size);
306                         if (yp_password.pw_uid == pw->pw_uid &&
307                             yp_password.pw_gid == pw->pw_gid) {
308                                 hit++;
309                                 snprintf(domain, YPMAXDOMAIN, "%s", tmp);
310                         }
311                 }
312         }
313
314         closedir(dird);
315         if (hit > 1) {
316                 yp_error("found same user in two different domains");
317                 return(NULL);
318         } else
319                 return((char *)&domain);
320 }
321
322 static int update_inplace(pw, domain)
323         struct passwd *pw;
324         char *domain;
325 {
326         DB *dbp = NULL;
327         DBT key = { NULL, 0 };
328         DBT data = { NULL, 0 };
329         char pwbuf[YPMAXRECORD];
330         char keybuf[20];
331         int i;
332         char *maps[] = { "master.passwd.byname", "master.passwd.byuid",
333                          "passwd.byname", "passwd.byuid" };
334
335         char *formats[] = { "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
336                             "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
337                             "%s:%s:%d:%d:%s:%s:%s", "%s:%s:%d:%d:%s:%s:%s" };
338         char *ptr = NULL;
339         char *yp_last = "YP_LAST_MODIFIED";
340         char yplastbuf[YPMAXRECORD];
341
342         snprintf(yplastbuf, sizeof(yplastbuf), "%lu", time(NULL));
343
344         for (i = 0; i < 4; i++) {
345
346                 if (i % 2) {
347                         snprintf(keybuf, sizeof(keybuf), "%ld", pw->pw_uid);
348                         key.data = (char *)&keybuf;
349                         key.size = strlen(keybuf);
350                 } else {
351                         key.data = pw->pw_name;
352                         key.size = strlen(pw->pw_name);
353                 }
354
355                 /*
356                  * XXX The passwd.byname and passwd.byuid maps come in
357                  * two flavors: secure and insecure. The secure version
358                  * has a '*' in the password field whereas the insecure one
359                  * has a real crypted password. The maps will be insecure
360                  * if they were built with 'unsecure = TRUE' enabled in
361                  * /var/yp/Makefile, but we'd have no way of knowing if
362                  * this has been done unless we were to try parsing the
363                  * Makefile, which is a disgusting thought. Instead, we
364                  * read the records from the maps, skip to the first ':'
365                  * in them, and then look at the character immediately
366                  * following it. If it's an '*' then the map is 'secure'
367                  * and we must not insert a real password into the pw_passwd
368                  * field. If it's not an '*', then we put the real crypted
369                  * password in.
370                  */
371                 if (yp_get_record(domain,maps[i],&key,&data,1) != YP_TRUE) {
372                         yp_error("couldn't read %s/%s: %s", domain,
373                                                 maps[i], strerror(errno));
374                         return(1);
375                 }
376
377                 if ((ptr = strchr(data.data, ':')) == NULL) {
378                         yp_error("no colon in passwd record?!");
379                         return(1);
380                 }
381
382                 /*
383                  * XXX Supposing we have more than one user with the same
384                  * UID? (Or more than one user with the same name?) We could
385                  * end up modifying the wrong record if were not careful.
386                  */
387                 if (i % 2) {
388                         if (strncmp(data.data, pw->pw_name,
389                                                         strlen(pw->pw_name))) {
390                                 yp_error("warning: found entry for UID %d \
391 in map %s@%s with wrong name (%.*s)", pw->pw_uid, maps[i], domain,
392                                         ptr - (char *)data.data, data.data);
393                                 yp_error("there may be more than one user \
394 with the same UID - continuing");
395                                 continue;
396                         }
397                 } else {
398                         /*
399                          * We're really being ultra-paranoid here.
400                          * This is generally a 'can't happen' condition.
401                          */
402                         snprintf(pwbuf, sizeof(pwbuf), ":%d:%d:", pw->pw_uid,
403                                                                   pw->pw_gid);
404                         if (!strstr(data.data, pwbuf)) {
405                                 yp_error("warning: found entry for user %s \
406 in map %s@%s with wrong UID", pw->pw_name, maps[i], domain);
407                                 yp_error("there may be more than one user
408 with the same name - continuing");
409                                 continue;
410                         }
411                 }
412
413                 if (i < 2) {
414                         snprintf(pwbuf, sizeof(pwbuf), formats[i],
415                            pw->pw_name, pw->pw_passwd, pw->pw_uid,
416                            pw->pw_gid, pw->pw_class, pw->pw_change,
417                            pw->pw_expire, pw->pw_gecos, pw->pw_dir,
418                            pw->pw_shell);
419                 } else {
420                         snprintf(pwbuf, sizeof(pwbuf), formats[i],
421                            pw->pw_name, *(ptr+1) == '*' ? "*" : pw->pw_passwd,
422                            pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir,
423                            pw->pw_shell);
424                 }
425
426 #define FLAGS O_RDWR|O_CREAT
427
428                 if ((dbp = yp_open_db_rw(domain, maps[i], FLAGS)) == NULL) {
429                         yp_error("couldn't open %s/%s r/w: %s",domain,
430                                                 maps[i],strerror(errno));
431                         return(1);
432                 }
433
434                 data.data = pwbuf;
435                 data.size = strlen(pwbuf);
436
437                 if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
438                         yp_error("failed to update record in %s/%s", domain,
439                                                                 maps[i]);
440                         (void)(dbp->close)(dbp);
441                         return(1);
442                 }
443
444                 key.data = yp_last;
445                 key.size = strlen(yp_last);
446                 data.data = (char *)&yplastbuf;
447                 data.size = strlen(yplastbuf);
448
449                 if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
450                         yp_error("failed to update timestamp in %s/%s", domain,
451                                                                 maps[i]);
452                         (void)(dbp->close)(dbp);
453                         return(1);
454                 }
455
456                 (void)(dbp->close)(dbp);
457         }
458
459         return(0);
460 }
461
462 static char *yp_mktmpnam()
463 {
464         static char path[MAXPATHLEN];
465         char *p;
466
467         sprintf(path,"%s",passfile);
468         if ((p = strrchr(path, '/')))
469                 ++p;
470         else
471                 p = path;
472         strcpy(p, "yppwtmp.XXXXXX");
473         return(mktemp(path));
474 }
475
476 int *
477 yppasswdproc_update_1_svc(yppasswd *argp, struct svc_req *rqstp)
478 {
479         static int  result;
480         struct sockaddr_in *rqhost;
481         DBT key, data;
482         int rval = 0;
483         int pfd, tfd;
484         int pid;
485         int passwd_changed = 0;
486         int shell_changed = 0;
487         int gecos_changed = 0;
488         char *oldshell = NULL;
489         char *oldgecos = NULL;
490         char *passfile_hold;
491         char passfile_buf[MAXPATHLEN + 2];
492         char *domain = yppasswd_domain;
493         static struct sockaddr_in clntaddr;
494         static struct timeval t_saved, t_test;
495
496         /*
497          * Normal user updates always use the 'default' master.passwd file.
498          */
499
500         passfile = passfile_default;
501         result = 1;
502
503         rqhost = svc_getcaller(rqstp->rq_xprt);
504
505         gettimeofday(&t_test, NULL);
506         if (!bcmp((char *)rqhost, (char *)&clntaddr,
507                                                 sizeof(struct sockaddr_in)) &&
508                 t_test.tv_sec > t_saved.tv_sec &&
509                 t_test.tv_sec - t_saved.tv_sec < 300) {
510
511                 bzero((char *)&clntaddr, sizeof(struct sockaddr_in));
512                 bzero((char *)&t_saved, sizeof(struct timeval));
513                 return(NULL);
514         }
515
516         bcopy((char *)rqhost, (char *)&clntaddr, sizeof(struct sockaddr_in));
517         gettimeofday(&t_saved, NULL);
518
519         if (yp_access(resvport ? "master.passwd.byname" : NULL, rqstp)) {
520                 yp_error("rejected update request from unauthorized host");
521                 svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
522                 return(&result);
523         }
524
525         /*
526          * Step one: find the user. (It's kinda pointless to
527          * proceed if the user doesn't exist.) We look for the
528          * user in the master.passwd.byname database, _NOT_ by
529          * using getpwent() and friends! We can't use getpwent()
530          * since the NIS master server is not guaranteed to be
531          * configured as an NIS client.
532          */
533
534         if (multidomain) {
535                 if ((domain = find_domain(&argp->newpw)) == NULL) {
536                         yp_error("multidomain lookup failed - aborting update");
537                         return(&result);
538                 } else
539                         yp_error("updating user %s in domain %s",
540                                         argp->newpw.pw_name, domain);
541         }
542
543         key.data = argp->newpw.pw_name;
544         key.size = strlen(argp->newpw.pw_name);
545
546         if ((rval=yp_get_record(domain,"master.passwd.byname",
547                         &key, &data, 0)) != YP_TRUE) {
548                 if (rval == YP_NOKEY) {
549                         yp_error("user %s not found in passwd database",
550                                 argp->newpw.pw_name);
551                 } else {
552                         yp_error("database access error: %s",
553                                 yperr_string(rval));
554                 }
555                 return(&result);
556         }
557
558         /* Nul terminate, please. */
559         *(char *)(data.data + data.size) = '\0';
560
561         copy_yp_pass(data.data, 1, data.size);
562
563         /* Step 2: check that the supplied oldpass is valid. */
564
565         if (strcmp(crypt(argp->oldpass, yp_password.pw_passwd),
566                                         yp_password.pw_passwd)) {
567                 yp_error("rejected change attempt -- bad password");
568                 yp_error("client address: %s username: %s",
569                           inet_ntoa(rqhost->sin_addr),
570                           argp->newpw.pw_name);
571                 return(&result);
572         }
573
574         /* Step 3: validate the arguments passed to us by the client. */
575
576         if (validate(&yp_password, &argp->newpw)) {
577                 yp_error("rejecting change attempt: bad arguments");
578                 yp_error("client address: %s username: %s",
579                          inet_ntoa(rqhost->sin_addr),
580                          argp->newpw.pw_name);
581                 svcerr_decode(rqstp->rq_xprt);
582                 return(&result);
583         }
584
585         /* Step 4: update the user's passwd structure. */
586
587         if (!no_chsh && strcmp(argp->newpw.pw_shell, yp_password.pw_shell)) {
588                 oldshell = yp_password.pw_shell;
589                 yp_password.pw_shell = argp->newpw.pw_shell;
590                 shell_changed++;
591         }
592
593
594         if (!no_chfn && strcmp(argp->newpw.pw_gecos, yp_password.pw_gecos)) {
595                 oldgecos = yp_password.pw_gecos;
596                 yp_password.pw_gecos = argp->newpw.pw_gecos;
597                 gecos_changed++;
598         }
599
600         if (strcmp(argp->newpw.pw_passwd, yp_password.pw_passwd)) {
601                 yp_password.pw_passwd = argp->newpw.pw_passwd;
602                 yp_password.pw_change = 0;
603                 passwd_changed++;
604         }
605
606         /*
607          * If the caller specified a domain other than our 'default'
608          * domain, change the path to master.passwd accordingly.
609          */
610
611         if (strcmp(domain, yppasswd_domain)) {
612                 snprintf(passfile_buf, sizeof(passfile_buf),
613                         "%s/%s/master.passwd", yp_dir, domain);
614                 passfile = (char *)&passfile_buf;
615         }
616
617         /* Step 5: make a new password file with the updated info. */
618
619         if ((pfd = pw_lock()) < 0) {
620                 return (&result);
621         }
622         if ((tfd = pw_tmp()) < 0) {
623                 return (&result);
624         }
625
626         if (pw_copy(pfd, tfd, &yp_password)) {
627                 yp_error("failed to created updated password file -- \
628 cleaning up and bailing out");
629                 unlink(tempname);
630                 return(&result);
631         }
632
633         passfile_hold = yp_mktmpnam();
634         rename(passfile, passfile_hold);
635         if (strcmp(passfile, _PATH_MASTERPASSWD)) {
636                 rename(tempname, passfile);
637         } else {
638                 if (pw_mkdb(argp->newpw.pw_name) < 0) {
639                         yp_error("pwd_mkdb failed");
640                         return(&result);
641                 }
642         }
643
644         if (inplace) {
645                 if ((rval = update_inplace(&yp_password, domain))) {
646                         yp_error("inplace update failed -- rebuilding maps");
647                 }
648         }
649
650         switch((pid = fork())) {
651         case 0:
652                 if (inplace && !rval) {
653                         execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
654                                 yppasswd_domain, "pushpw", (char *)NULL);
655                 } else {
656                         execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
657                                 yppasswd_domain, (char *)NULL);
658                 }
659                 yp_error("couldn't exec map update process: %s",
660                                         strerror(errno));
661                 unlink(passfile);
662                 rename(passfile_hold, passfile);
663                 exit(1);
664                 break;
665         case -1:
666                 yp_error("fork() failed: %s", strerror(errno));
667                 unlink(passfile);
668                 rename(passfile_hold, passfile);
669                 return(&result);
670                 break;
671         default:
672                 unlink(passfile_hold);
673                 break;
674         }
675
676         if (verbose) {
677                 yp_error("update completed for user %s (uid %d):",
678                                                 argp->newpw.pw_name,
679                                                 argp->newpw.pw_uid);
680
681                 if (passwd_changed)
682                         yp_error("password changed");
683
684                 if (gecos_changed)
685                         yp_error("gecos changed ('%s' -> '%s')",
686                                         oldgecos, argp->newpw.pw_gecos);
687
688                 if (shell_changed)
689                         yp_error("shell changed ('%s' -> '%s')",
690                                         oldshell, argp->newpw.pw_shell);
691         }
692
693         result = 0;
694         return (&result);
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", (char *)NULL);
845                 } else {
846                         execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
847                                 argp->domain, (char *)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 }