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