]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.sbin/nfsuserd/nfsuserd.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / usr.sbin / nfsuserd / nfsuserd.c
1 /*-
2  * Copyright (c) 2009 Rick Macklem, University of Guelph
3  * 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  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/errno.h>
33 #include <sys/linker.h>
34 #include <sys/module.h>
35 #include <sys/mount.h>
36 #include <sys/socket.h>
37 #include <sys/socketvar.h>
38 #include <sys/time.h>
39 #include <sys/ucred.h>
40 #include <sys/vnode.h>
41 #include <sys/wait.h>
42
43 #include <nfs/nfssvc.h>
44
45 #include <rpc/rpc.h>
46
47 #include <fs/nfs/rpcv2.h>
48 #include <fs/nfs/nfsproto.h>
49 #include <fs/nfs/nfskpiport.h>
50 #include <fs/nfs/nfs.h>
51
52 #include <ctype.h>
53 #include <err.h>
54 #include <grp.h>
55 #include <netdb.h>
56 #include <pwd.h>
57 #include <signal.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <syslog.h>
62 #include <unistd.h>
63
64 /*
65  * This program loads the password and group databases into the kernel
66  * for NFS V4.
67  */
68
69 static void     cleanup_term(int);
70 static void     usage(void);
71 static void     nfsuserdsrv(struct svc_req *, SVCXPRT *);
72 static bool_t   xdr_getid(XDR *, caddr_t);
73 static bool_t   xdr_getname(XDR *, caddr_t);
74 static bool_t   xdr_retval(XDR *, caddr_t);
75
76 #define MAXNAME         1024
77 #define MAXNFSUSERD     20
78 #define DEFNFSUSERD     4
79 #define DEFUSERMAX      200
80 #define DEFUSERTIMEOUT  (1 * 60)
81 struct info {
82         long    id;
83         long    retval;
84         char    name[MAXNAME + 1];
85 };
86
87 u_char *dnsname = "default.domain";
88 u_char *defaultuser = "nobody";
89 uid_t defaultuid = (uid_t)32767;
90 u_char *defaultgroup = "nogroup";
91 gid_t defaultgid = (gid_t)32767;
92 int verbose = 0, im_a_slave = 0, nfsuserdcnt = -1, forcestart = 0;
93 int defusertimeout = DEFUSERTIMEOUT;
94 pid_t slaves[MAXNFSUSERD];
95
96 int
97 main(int argc, char *argv[])
98 {
99         int i;
100         int error, len, mustfreeai = 0;
101         struct nfsd_idargs nid;
102         struct passwd *pwd;
103         struct group *grp;
104         int sock, one = 1;
105         SVCXPRT *udptransp;
106         u_short portnum;
107         sigset_t signew;
108         char hostname[MAXHOSTNAMELEN + 1], *cp;
109         struct addrinfo *aip, hints;
110
111         if (modfind("nfscommon") < 0) {
112                 /* Not present in kernel, try loading it */
113                 if (kldload("nfscommon") < 0 ||
114                     modfind("nfscommon") < 0)
115                         errx(1, "Experimental nfs subsystem is not available");
116         }
117
118         /*
119          * First, figure out what our domain name and Kerberos Realm
120          * seem to be. Command line args may override these later.
121          */
122         if (gethostname(hostname, MAXHOSTNAMELEN) == 0) {
123                 if ((cp = strchr(hostname, '.')) != NULL &&
124                     *(cp + 1) != '\0') {
125                         dnsname = cp + 1;
126                 } else {
127                         memset((void *)&hints, 0, sizeof (hints));
128                         hints.ai_flags = AI_CANONNAME;
129                         error = getaddrinfo(hostname, NULL, &hints, &aip);
130                         if (error == 0) {
131                             if (aip->ai_canonname != NULL &&
132                                 (cp = strchr(aip->ai_canonname, '.')) != NULL
133                                 && *(cp + 1) != '\0') {
134                                         dnsname = cp + 1;
135                                         mustfreeai = 1;
136                                 } else {
137                                         freeaddrinfo(aip);
138                                 }
139                         }
140                 }
141         }
142         nid.nid_usermax = DEFUSERMAX;
143         nid.nid_usertimeout = defusertimeout;
144
145         argc--;
146         argv++;
147         while (argc >= 1) {
148                 if (!strcmp(*argv, "-domain")) {
149                         if (argc == 1)
150                                 usage();
151                         argc--;
152                         argv++;
153                         strncpy(hostname, *argv, MAXHOSTNAMELEN);
154                         hostname[MAXHOSTNAMELEN] = '\0';
155                         dnsname = hostname;
156                 } else if (!strcmp(*argv, "-verbose")) {
157                         verbose = 1;
158                 } else if (!strcmp(*argv, "-force")) {
159                         forcestart = 1;
160                 } else if (!strcmp(*argv, "-usermax")) {
161                         if (argc == 1)
162                                 usage();
163                         argc--;
164                         argv++;
165                         i = atoi(*argv);
166                         if (i < 10 || i > 100000) {
167                                 fprintf(stderr,
168                                     "usermax %d out of range 10<->100000\n", i);
169                                 usage();
170                         }
171                         nid.nid_usermax = i;
172                 } else if (!strcmp(*argv, "-usertimeout")) {
173                         if (argc == 1)
174                                 usage();
175                         argc--;
176                         argv++;
177                         i = atoi(*argv);
178                         if (i < 0 || i > 100000) {
179                                 fprintf(stderr,
180                                     "usertimeout %d out of range 0<->100000\n",
181                                     i);
182                                 usage();
183                         }
184                         nid.nid_usertimeout = defusertimeout = i * 60;
185                 } else if (nfsuserdcnt == -1) {
186                         nfsuserdcnt = atoi(*argv);
187                         if (nfsuserdcnt < 1)
188                                 usage();
189                         if (nfsuserdcnt > MAXNFSUSERD) {
190                                 warnx("nfsuserd count %d; reset to %d",
191                                     nfsuserdcnt, DEFNFSUSERD);
192                                 nfsuserdcnt = DEFNFSUSERD;
193                         }
194                 } else {
195                         usage();
196                 }
197                 argc--;
198                 argv++;
199         }
200         if (nfsuserdcnt < 1)
201                 nfsuserdcnt = DEFNFSUSERD;
202
203         /*
204          * Strip off leading and trailing '.'s in domain name and map
205          * alphabetics to lower case.
206          */
207         while (*dnsname == '.')
208                 dnsname++;
209         if (*dnsname == '\0')
210                 errx(1, "Domain name all '.'");
211         len = strlen(dnsname);
212         cp = dnsname + len - 1;
213         while (*cp == '.') {
214                 *cp = '\0';
215                 len--;
216                 cp--;
217         }
218         for (i = 0; i < len; i++) {
219                 if (!isascii(dnsname[i]))
220                         errx(1, "Domain name has non-ascii char");
221                 if (isupper(dnsname[i]))
222                         dnsname[i] = tolower(dnsname[i]);
223         }
224
225         /*
226          * If the nfsuserd died off ungracefully, this is necessary to
227          * get them to start again.
228          */
229         if (forcestart && nfssvc(NFSSVC_NFSUSERDDELPORT, NULL) < 0)
230                 errx(1, "Can't do nfssvc() to delete the port");
231
232         if (verbose)
233                 fprintf(stderr,
234                     "nfsuserd: domain=%s usermax=%d usertimeout=%d\n",
235                     dnsname, nid.nid_usermax, nid.nid_usertimeout);
236
237         for (i = 0; i < nfsuserdcnt; i++)
238                 slaves[i] = (pid_t)-1;
239
240         /*
241          * Set up the service port to accept requests via UDP from
242          * localhost (127.0.0.1).
243          */
244         if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
245                 err(1, "cannot create udp socket");
246
247         /*
248          * Not sure what this does, so I'll leave it here for now.
249          */
250         setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
251         
252         if ((udptransp = svcudp_create(sock)) == NULL)
253                 err(1, "Can't set up socket");
254
255         /*
256          * By not specifying a protocol, it is linked into the
257          * dispatch queue, but not registered with portmapper,
258          * which is just what I want.
259          */
260         if (!svc_register(udptransp, RPCPROG_NFSUSERD, RPCNFSUSERD_VERS,
261             nfsuserdsrv, 0))
262                 err(1, "Can't register nfsuserd");
263
264         /*
265          * Tell the kernel what my port# is.
266          */
267         portnum = htons(udptransp->xp_port);
268 #ifdef DEBUG
269         printf("portnum=0x%x\n", portnum);
270 #else
271         if (nfssvc(NFSSVC_NFSUSERDPORT, (caddr_t)&portnum) < 0) {
272                 if (errno == EPERM) {
273                         fprintf(stderr,
274                             "Can't start nfsuserd when already running");
275                         fprintf(stderr,
276                             " If not running, use the -force option.\n");
277                 } else {
278                         fprintf(stderr, "Can't do nfssvc() to add port\n");
279                 }
280                 exit(1);
281         }
282 #endif
283
284         pwd = getpwnam(defaultuser);
285         if (pwd)
286                 nid.nid_uid = pwd->pw_uid;
287         else
288                 nid.nid_uid = defaultuid;
289         grp = getgrnam(defaultgroup);
290         if (grp)
291                 nid.nid_gid = grp->gr_gid;
292         else
293                 nid.nid_gid = defaultgid;
294         nid.nid_name = dnsname;
295         nid.nid_namelen = strlen(nid.nid_name);
296         nid.nid_flag = NFSID_INITIALIZE;
297 #ifdef DEBUG
298         printf("Initialize uid=%d gid=%d dns=%s\n", nid.nid_uid, nid.nid_gid, 
299             nid.nid_name);
300 #else
301         error = nfssvc(NFSSVC_IDNAME, &nid);
302         if (error)
303                 errx(1, "Can't initialize nfs user/groups");
304 #endif
305
306         i = 0;
307         /*
308          * Loop around adding all groups.
309          */
310         setgrent();
311         while (i < nid.nid_usermax && (grp = getgrent())) {
312                 nid.nid_gid = grp->gr_gid;
313                 nid.nid_name = grp->gr_name;
314                 nid.nid_namelen = strlen(grp->gr_name);
315                 nid.nid_flag = NFSID_ADDGID;
316 #ifdef DEBUG
317                 printf("add gid=%d name=%s\n", nid.nid_gid, nid.nid_name);
318 #else
319                 error = nfssvc(NFSSVC_IDNAME, &nid);
320                 if (error)
321                         errx(1, "Can't add group %s", grp->gr_name);
322 #endif
323                 i++;
324         }
325
326         /*
327          * Loop around adding all users.
328          */
329         setpwent();
330         while (i < nid.nid_usermax && (pwd = getpwent())) {
331                 nid.nid_uid = pwd->pw_uid;
332                 nid.nid_name = pwd->pw_name;
333                 nid.nid_namelen = strlen(pwd->pw_name);
334                 nid.nid_flag = NFSID_ADDUID;
335 #ifdef DEBUG
336                 printf("add uid=%d name=%s\n", nid.nid_uid, nid.nid_name);
337 #else
338                 error = nfssvc(NFSSVC_IDNAME, &nid);
339                 if (error)
340                         errx(1, "Can't add user %s", pwd->pw_name);
341 #endif
342                 i++;
343         }
344
345         /*
346          * I should feel guilty for not calling this for all the above exit()
347          * upon error cases, but I don't.
348          */
349         if (mustfreeai)
350                 freeaddrinfo(aip);
351
352 #ifdef DEBUG
353         exit(0);
354 #endif
355         /*
356          * Temporarily block SIGUSR1 and SIGCHLD, so slaves[] can't
357          * end up bogus.
358          */
359         sigemptyset(&signew);
360         sigaddset(&signew, SIGUSR1);
361         sigaddset(&signew, SIGCHLD);
362         sigprocmask(SIG_BLOCK, &signew, NULL);
363
364         daemon(0, 0);
365         (void)signal(SIGHUP, SIG_IGN);
366         (void)signal(SIGINT, SIG_IGN);
367         (void)signal(SIGQUIT, SIG_IGN);
368         (void)signal(SIGTERM, SIG_IGN);
369         (void)signal(SIGUSR1, cleanup_term);
370         (void)signal(SIGCHLD, cleanup_term);
371
372         openlog("nfsuserd:", LOG_PID, LOG_DAEMON);
373
374         /*
375          * Fork off the slave daemons that do the work. All the master
376          * does is kill them off and cleanup.
377          */
378         for (i = 0; i < nfsuserdcnt; i++) {
379                 slaves[i] = fork();
380                 if (slaves[i] == 0) {
381                         im_a_slave = 1;
382                         setproctitle("slave");
383                         sigemptyset(&signew);
384                         sigaddset(&signew, SIGUSR1);
385                         sigprocmask(SIG_UNBLOCK, &signew, NULL);
386
387                         /*
388                          * and away we go.
389                          */
390                         svc_run();
391                         syslog(LOG_ERR, "nfsuserd died: %m");
392                         exit(1);
393                 } else if (slaves[i] < 0) {
394                         syslog(LOG_ERR, "fork: %m");
395                 }
396         }
397
398         /*
399          * Just wait for SIGUSR1 or a child to die and then...
400          * As the Governor of California would say, "Terminate them".
401          */
402         setproctitle("master");
403         sigemptyset(&signew);
404         while (1)
405                 sigsuspend(&signew);
406 }
407
408 /*
409  * The nfsuserd rpc service
410  */
411 static void
412 nfsuserdsrv(struct svc_req *rqstp, SVCXPRT *transp)
413 {
414         struct passwd *pwd;
415         struct group *grp;
416         int error;
417         u_short sport;
418         struct info info;
419         struct nfsd_idargs nid;
420         u_int32_t saddr;
421
422         /*
423          * Only handle requests from 127.0.0.1 on a reserved port number.
424          * (Since a reserved port # at localhost implies a client with
425          *  local root, there won't be a security breach. This is about
426          *  the only case I can think of where a reserved port # means
427          *  something.)
428          */
429         sport = ntohs(transp->xp_raddr.sin_port);
430         saddr = ntohl(transp->xp_raddr.sin_addr.s_addr);
431         if ((rqstp->rq_proc != NULLPROC && sport >= IPPORT_RESERVED) ||
432             saddr != 0x7f000001) {
433                 syslog(LOG_ERR, "req from ip=0x%x port=%d\n", saddr, sport);
434                 svcerr_weakauth(transp);
435                 return;
436         }
437         switch (rqstp->rq_proc) {
438         case NULLPROC:
439                 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
440                         syslog(LOG_ERR, "Can't send reply");
441                 return;
442         case RPCNFSUSERD_GETUID:
443                 if (!svc_getargs(transp, (xdrproc_t)xdr_getid,
444                     (caddr_t)&info)) {
445                         svcerr_decode(transp);
446                         return;
447                 }
448                 pwd = getpwuid((uid_t)info.id);
449                 info.retval = 0;
450                 if (pwd != NULL) {
451                         nid.nid_usertimeout = defusertimeout;
452                         nid.nid_uid = pwd->pw_uid;
453                         nid.nid_name = pwd->pw_name;
454                 } else {
455                         nid.nid_usertimeout = 5;
456                         nid.nid_uid = (uid_t)info.id;
457                         nid.nid_name = defaultuser;
458                 }
459                 nid.nid_namelen = strlen(nid.nid_name);
460                 nid.nid_flag = NFSID_ADDUID;
461                 error = nfssvc(NFSSVC_IDNAME, &nid);
462                 if (error) {
463                         info.retval = error;
464                         syslog(LOG_ERR, "Can't add user %s\n", pwd->pw_name);
465                 } else if (verbose) {
466                         syslog(LOG_ERR,"Added uid=%d name=%s\n",
467                             nid.nid_uid, nid.nid_name);
468                 }
469                 if (!svc_sendreply(transp, (xdrproc_t)xdr_retval,
470                     (caddr_t)&info))
471                         syslog(LOG_ERR, "Can't send reply");
472                 return;
473         case RPCNFSUSERD_GETGID:
474                 if (!svc_getargs(transp, (xdrproc_t)xdr_getid,
475                     (caddr_t)&info)) {
476                         svcerr_decode(transp);
477                         return;
478                 }
479                 grp = getgrgid((gid_t)info.id);
480                 info.retval = 0;
481                 if (grp != NULL) {
482                         nid.nid_usertimeout = defusertimeout;
483                         nid.nid_gid = grp->gr_gid;
484                         nid.nid_name = grp->gr_name;
485                 } else {
486                         nid.nid_usertimeout = 5;
487                         nid.nid_gid = (gid_t)info.id;
488                         nid.nid_name = defaultgroup;
489                 }
490                 nid.nid_namelen = strlen(nid.nid_name);
491                 nid.nid_flag = NFSID_ADDGID;
492                 error = nfssvc(NFSSVC_IDNAME, &nid);
493                 if (error) {
494                         info.retval = error;
495                         syslog(LOG_ERR, "Can't add group %s\n",
496                             grp->gr_name);
497                 } else if (verbose) {
498                         syslog(LOG_ERR,"Added gid=%d name=%s\n",
499                             nid.nid_gid, nid.nid_name);
500                 }
501                 if (!svc_sendreply(transp, (xdrproc_t)xdr_retval,
502                     (caddr_t)&info))
503                         syslog(LOG_ERR, "Can't send reply");
504                 return;
505         case RPCNFSUSERD_GETUSER:
506                 if (!svc_getargs(transp, (xdrproc_t)xdr_getname,
507                     (caddr_t)&info)) {
508                         svcerr_decode(transp);
509                         return;
510                 }
511                 pwd = getpwnam(info.name);
512                 info.retval = 0;
513                 if (pwd != NULL) {
514                         nid.nid_usertimeout = defusertimeout;
515                         nid.nid_uid = pwd->pw_uid;
516                         nid.nid_name = pwd->pw_name;
517                 } else {
518                         nid.nid_usertimeout = 5;
519                         nid.nid_uid = defaultuid;
520                         nid.nid_name = info.name;
521                 }
522                 nid.nid_namelen = strlen(nid.nid_name);
523                 nid.nid_flag = NFSID_ADDUSERNAME;
524                 error = nfssvc(NFSSVC_IDNAME, &nid);
525                 if (error) {
526                         info.retval = error;
527                         syslog(LOG_ERR, "Can't add user %s\n", pwd->pw_name);
528                 } else if (verbose) {
529                         syslog(LOG_ERR,"Added uid=%d name=%s\n",
530                             nid.nid_uid, nid.nid_name);
531                 }
532                 if (!svc_sendreply(transp, (xdrproc_t)xdr_retval,
533                     (caddr_t)&info))
534                         syslog(LOG_ERR, "Can't send reply");
535                 return;
536         case RPCNFSUSERD_GETGROUP:
537                 if (!svc_getargs(transp, (xdrproc_t)xdr_getname,
538                     (caddr_t)&info)) {
539                         svcerr_decode(transp);
540                         return;
541                 }
542                 grp = getgrnam(info.name);
543                 info.retval = 0;
544                 if (grp != NULL) {
545                         nid.nid_usertimeout = defusertimeout;
546                         nid.nid_gid = grp->gr_gid;
547                         nid.nid_name = grp->gr_name;
548                 } else {
549                         nid.nid_usertimeout = 5;
550                         nid.nid_gid = defaultgid;
551                         nid.nid_name = info.name;
552                 }
553                 nid.nid_namelen = strlen(nid.nid_name);
554                 nid.nid_flag = NFSID_ADDGROUPNAME;
555                 error = nfssvc(NFSSVC_IDNAME, &nid);
556                 if (error) {
557                         info.retval = error;
558                         syslog(LOG_ERR, "Can't add group %s\n",
559                             grp->gr_name);
560                 } else if (verbose) {
561                         syslog(LOG_ERR,"Added gid=%d name=%s\n",
562                             nid.nid_gid, nid.nid_name);
563                 }
564                 if (!svc_sendreply(transp, (xdrproc_t)xdr_retval,
565                     (caddr_t)&info))
566                         syslog(LOG_ERR, "Can't send reply");
567                 return;
568         default:
569                 svcerr_noproc(transp);
570                 return;
571         };
572 }
573
574 /*
575  * Xdr routine to get an id number
576  */
577 static bool_t
578 xdr_getid(XDR *xdrsp, caddr_t cp)
579 {
580         struct info *ifp = (struct info *)cp;
581
582         return (xdr_long(xdrsp, &ifp->id));
583 }
584
585 /*
586  * Xdr routine to get a user name
587  */
588 static bool_t
589 xdr_getname(XDR *xdrsp, caddr_t cp)
590 {
591         struct info *ifp = (struct info *)cp;
592         long len;
593
594         if (!xdr_long(xdrsp, &len))
595                 return (0);
596         if (len > MAXNAME)
597                 return (0);
598         if (!xdr_opaque(xdrsp, ifp->name, len))
599                 return (0);
600         ifp->name[len] = '\0';
601         return (1);
602 }
603
604 /*
605  * Xdr routine to return the value.
606  */
607 static bool_t
608 xdr_retval(XDR *xdrsp, caddr_t cp)
609 {
610         struct info *ifp = (struct info *)cp;
611         long val;
612
613         val = ifp->retval;
614         return (xdr_long(xdrsp, &val));
615 }
616
617 /*
618  * cleanup_term() called via SIGUSR1.
619  */
620 static void
621 cleanup_term(int signo __unused)
622 {
623         int i, cnt;
624
625         if (im_a_slave)
626                 exit(0);
627
628         /*
629          * Ok, so I'm the master.
630          * As the Governor of California might say, "Terminate them".
631          */
632         cnt = 0;
633         for (i = 0; i < nfsuserdcnt; i++) {
634                 if (slaves[i] != (pid_t)-1) {
635                         cnt++;
636                         kill(slaves[i], SIGUSR1);
637                 }
638         }
639
640         /*
641          * and wait for them to die
642          */
643         for (i = 0; i < cnt; i++)
644                 wait3(NULL, 0, NULL);
645
646         /*
647          * Finally, get rid of the socket
648          */
649         if (nfssvc(NFSSVC_NFSUSERDDELPORT, NULL) < 0) {
650                 syslog(LOG_ERR, "Can't do nfssvc() to delete the port\n");
651                 exit(1);
652         }
653         exit(0);
654 }
655
656 static void
657 usage(void)
658 {
659
660         errx(1,
661             "usage: nfsuserd [-usermax cache_size] [-usertimeout minutes] [-verbose] [-domain domain_name] [n]");
662 }