utils: Return status 0 on clean exits
[nfs-utils.git] / utils / gssd / svcgssd.c
1 /*
2   gssd.c
3
4   Copyright (c) 2000 The Regents of the University of Michigan.
5   All rights reserved.
6
7   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
8   Copyright (c) 2002 Andy Adamson <andros@UMICH.EDU>.
9   Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>.
10   Copyright (c) 2002 J. Bruce Fields <bfields@UMICH.EDU>.
11   All rights reserved, all wrongs reversed.
12
13   Redistribution and use in source and binary forms, with or without
14   modification, are permitted provided that the following conditions
15   are met:
16
17   1. Redistributions of source code must retain the above copyright
18      notice, this list of conditions and the following disclaimer.
19   2. Redistributions in binary form must reproduce the above copyright
20      notice, this list of conditions and the following disclaimer in the
21      documentation and/or other materials provided with the distribution.
22   3. Neither the name of the University nor the names of its
23      contributors may be used to endorse or promote products derived
24      from this software without specific prior written permission.
25
26   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
27   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
28   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
33   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37
38 */
39
40 #ifdef HAVE_CONFIG_H
41 #include <config.h>
42 #endif  /* HAVE_CONFIG_H */
43
44 #include <sys/param.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/socket.h>
48 #include <rpc/rpc.h>
49 #include <fcntl.h>
50 #include <errno.h>
51
52
53 #include <unistd.h>
54 #include <err.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <signal.h>
59 #include <nfsidmap.h>
60 #include "nfslib.h"
61 #include "svcgssd.h"
62 #include "gss_util.h"
63 #include "err_util.h"
64
65 /*
66  * mydaemon creates a pipe between the partent and child
67  * process. The parent process will wait until the
68  * child dies or writes a '1' on the pipe signaling
69  * that it started successfully.
70  */
71 int pipefds[2] = { -1, -1};
72
73 static void
74 mydaemon(int nochdir, int noclose)
75 {
76         int pid, status, tempfd;
77
78         if (pipe(pipefds) < 0) {
79                 printerr(1, "mydaemon: pipe() failed: errno %d (%s)\n",
80                         errno, strerror(errno));
81                 exit(1);
82         }
83         if ((pid = fork ()) < 0) {
84                 printerr(1, "mydaemon: fork() failed: errno %d (%s)\n",
85                         errno, strerror(errno));
86                 exit(1);
87         }
88
89         if (pid != 0) {
90                 /*
91                  * Parent. Wait for status from child.
92                  */
93                 close(pipefds[1]);
94                 if (read(pipefds[0], &status, 1) != 1)
95                         exit(1);
96                 exit (0);
97         }
98         /* Child.       */
99         close(pipefds[0]);
100         setsid ();
101         if (nochdir == 0) {
102                 if (chdir ("/") == -1) {
103                         printerr(1, "mydaemon: chdir() failed: errno %d (%s)\n",
104                                 errno, strerror(errno));
105                         exit(1);
106                 }
107         }
108
109         while (pipefds[1] <= 2) {
110                 pipefds[1] = dup(pipefds[1]);
111                 if (pipefds[1] < 0) {
112                         printerr(1, "mydaemon: dup() failed: errno %d (%s)\n",
113                                 errno, strerror(errno));
114                         exit(1);
115                 }
116         }
117
118         if (noclose == 0) {
119                 tempfd = open("/dev/null", O_RDWR);
120                 if (tempfd >= 0) {
121                         dup2(tempfd, 0);
122                         dup2(tempfd, 1);
123                         dup2(tempfd, 2);
124                         close(tempfd);
125                 } else {
126                         printerr(1, "mydaemon: can't open /dev/null: errno %d "
127                                     "(%s)\n", errno, strerror(errno));
128                         exit(1);
129                 }
130         }
131
132         return;
133 }
134
135 static void
136 release_parent(void)
137 {
138         int status;
139
140         if (pipefds[1] > 0) {
141                 if (write(pipefds[1], &status, 1) != 1) {
142                         printerr(1, 
143                                 "WARN: writing to parent pipe failed: errno %d (%s)\n",
144                                 errno, strerror(errno));
145                 }
146                 close(pipefds[1]);
147                 pipefds[1] = -1;
148         }
149 }
150
151 void
152 sig_die(int signal)
153 {
154         /* destroy krb5 machine creds */
155         printerr(1, "exiting on signal %d\n", signal);
156         exit(0);
157 }
158
159 void
160 sig_hup(int signal)
161 {
162         /* don't exit on SIGHUP */
163         printerr(1, "Received SIGHUP(%d)... Ignoring.\n", signal);
164         return;
165 }
166
167 static void
168 usage(char *progname)
169 {
170         fprintf(stderr, "usage: %s [-n] [-f] [-v] [-r] [-i] [-p principal]\n",
171                 progname);
172         exit(1);
173 }
174
175 int
176 main(int argc, char *argv[])
177 {
178         int get_creds = 1;
179         int fg = 0;
180         int verbosity = 0;
181         int rpc_verbosity = 0;
182         int idmap_verbosity = 0;
183         int opt, status;
184         extern char *optarg;
185         char *progname;
186         char *principal = NULL;
187
188         while ((opt = getopt(argc, argv, "fivrnp:")) != -1) {
189                 switch (opt) {
190                         case 'f':
191                                 fg = 1;
192                                 break;
193                         case 'i':
194                                 idmap_verbosity++;
195                                 break;
196                         case 'n':
197                                 get_creds = 0;
198                                 break;
199                         case 'v':
200                                 verbosity++;
201                                 break;
202                         case 'r':
203                                 rpc_verbosity++;
204                                 break;
205                         case 'p':
206                                 principal = optarg;
207                                 break;
208                         default:
209                                 usage(argv[0]);
210                                 break;
211                 }
212         }
213
214         if ((progname = strrchr(argv[0], '/')))
215                 progname++;
216         else
217                 progname = argv[0];
218
219         initerr(progname, verbosity, fg);
220 #ifdef HAVE_AUTHGSS_SET_DEBUG_LEVEL
221         if (verbosity && rpc_verbosity == 0)
222                 rpc_verbosity = verbosity;
223         authgss_set_debug_level(rpc_verbosity);
224 #else
225         if (rpc_verbosity > 0)
226                 printerr(0, "Warning: rpcsec_gss library does not "
227                             "support setting debug level\n");
228 #endif
229 #ifdef HAVE_NFS4_SET_DEBUG
230                 if (verbosity && idmap_verbosity == 0)
231                         idmap_verbosity = verbosity;
232         nfs4_set_debug(idmap_verbosity, NULL);
233 #else
234         if (idmap_verbosity > 0)
235                 printerr(0, "Warning: your nfsidmap library does not "
236                             "support setting debug level\n");
237 #endif
238
239         if (gssd_check_mechs() != 0) {
240                 printerr(0, "ERROR: Problem with gssapi library\n");
241                 exit(1);
242         }
243
244         if (!fg)
245                 mydaemon(0, 0);
246
247         signal(SIGINT, sig_die);
248         signal(SIGTERM, sig_die);
249         signal(SIGHUP, sig_hup);
250
251         if (get_creds) {
252                 if (principal)
253                         status = gssd_acquire_cred(principal, 
254                                 ((const gss_OID)GSS_C_NT_USER_NAME));
255                 else
256                         status = gssd_acquire_cred(GSSD_SERVICE_NAME, 
257                                 (const gss_OID)GSS_C_NT_HOSTBASED_SERVICE);
258                 if (status == FALSE) {
259                         printerr(0, "unable to obtain root (machine) credentials\n");
260                         printerr(0, "do you have a keytab entry for "
261                                 "nfs/<your.host>@<YOUR.REALM> in "
262                                 "/etc/krb5.keytab?\n");
263                         exit(1);
264                 }
265         } else {
266                 status = gssd_acquire_cred(NULL,
267                         (const gss_OID)GSS_C_NT_HOSTBASED_SERVICE);
268                 if (status == FALSE) {
269                         printerr(0, "unable to obtain nameless credentials\n");
270                         exit(1);
271                 }
272         }
273
274         if (!fg)
275                 release_parent();
276
277         nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
278         gssd_run();
279         printerr(0, "gssd_run returned!\n");
280         abort();
281 }