1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * Usermode daemon which assists the kernel when handling gssapi calls.
28 * It is gssd that actually implements all gssapi calls.
29 * Some calls, such as gss_sign, are implemented in the kernel on a per
30 * mechanism basis.
31 */
32
33 #include <stdio.h>
34 #include <rpc/rpc.h>
35 #include <rpc/rpc_com.h>
36 #include <sys/syslog.h>
37 #include <sys/termios.h>
38 #include <unistd.h>
39 #include <sys/utsname.h>
40 #include <sys/systeminfo.h>
41 #include <stdlib.h>
42 #include <stropts.h>
43 #include <fcntl.h>
44 #include <strings.h>
45 #include <signal.h>
46 #include <syslog.h>
47 #include "gssd.h"
48
49 int gssd_debug = 0; /* enable debugging printfs */
50 extern void gsscred_set_options(void);
51
52 void gssprog_1();
53 void gssd_setup(char *);
54 static void usage(void);
55 static void daemonize_start();
56 static void daemonize_ready(unsigned char status);
57 extern int svc_create_local_service();
58
59 /* following declarations needed in rpcgen-generated code */
60 int _rpcpmstart = 0; /* Started by a port monitor ? */
61 int _rpcfdtype; /* Whether Stream or Datagram ? */
62 int _rpcsvcdirty; /* Still serving ? */
63 mutex_t _svcstate_lock = ERRORCHECKMUTEX;
64
65 static void
66 /* LINTED */
catch_hup(int sig_num)67 catch_hup(int sig_num)
68 {
69 sigset_t mask_set; /* used to set a signal masking set. */
70 sigset_t old_set; /* used to store the old mask set. */
71
72 /* re-set the signal handler again to catch_hup, for next time */
73 (void) signal(SIGHUP, catch_hup);
74 /* mask any further signals while we're inside the handler. */
75 (void) sigfillset(&mask_set);
76 (void) sigprocmask(SIG_SETMASK, &mask_set, &old_set);
77
78 gsscred_set_options();
79
80 /* let admin know the sighup was caught and conf file re-read */
81 syslog(LOG_INFO,
82 "catch_hup: read gsscred.conf opts");
83 if (gssd_debug)
84 (void) fprintf(stderr, "catch_hup: read gsscred.conf opts");
85
86 (void) sigprocmask(SIG_SETMASK, &old_set, NULL);
87 }
88
89
90 int
main(int argc,char ** argv)91 main(int argc, char **argv)
92 {
93 register SVCXPRT *transp;
94 int maxrecsz = RPC_MAXDATASIZE;
95 extern int optind;
96 int c;
97 char mname[FMNAMESZ + 1];
98
99 /* set locale and domain for internationalization */
100 setlocale(LC_ALL, "");
101 textdomain(TEXT_DOMAIN);
102
103 /*
104 * Take special note that "getuid()" is called here. This call is used
105 * rather than app_krb5_user_uid(), to ensure gssd(8) is running as
106 * root.
107 */
108 #ifdef DEBUG
109 (void) setuid(0); /* DEBUG: set ruid to root */
110 #endif /* DEBUG */
111 if (getuid()) {
112 (void) fprintf(stderr,
113 gettext("[%s] must be run as root\n"), argv[0]);
114 #ifdef DEBUG
115 (void) fprintf(stderr, gettext(" warning only\n"));
116 #else /* DEBUG */
117 exit(1);
118 #endif /* DEBUG */
119 }
120
121 gssd_setup(argv[0]);
122
123 while ((c = getopt(argc, argv, "d")) != -1)
124 switch (c) {
125 case 'd':
126 /* turn on debugging */
127 gssd_debug = 1;
128 break;
129 default:
130 usage();
131 }
132
133 if (optind != argc) {
134 usage();
135 }
136
137 gsscred_set_options();
138 (void) signal(SIGHUP, catch_hup);
139
140 /*
141 * Started by inetd if name of module just below stream
142 * head is either a sockmod or timod.
143 */
144 if (!ioctl(0, I_LOOK, mname) && ((strcmp(mname, "sockmod") == 0) ||
145 (strcmp(mname, "timod") == 0))) {
146
147 char *netid;
148 struct netconfig *nconf;
149
150 openlog("gssd", LOG_PID, LOG_DAEMON);
151
152 if ((netid = getenv("NLSPROVIDER")) == NULL) {
153 netid = "ticotsord";
154 }
155
156 if ((nconf = getnetconfigent(netid)) == NULL) {
157 syslog(LOG_ERR, gettext("cannot get transport info"));
158 exit(1);
159 }
160
161 if (strcmp(mname, "sockmod") == 0) {
162 if (ioctl(0, I_POP, 0) || ioctl(0, I_PUSH, "timod")) {
163 syslog(LOG_ERR,
164 gettext("could not get the "
165 "right module"));
166 exit(1);
167 }
168 }
169 if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrecsz)) {
170 syslog(LOG_ERR,
171 gettext("unable to set RPC max record size"));
172 exit(1);
173 }
174 /* XXX - is nconf even needed here? */
175 if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) {
176 syslog(LOG_ERR, gettext("cannot create server handle"));
177 exit(1);
178 }
179
180 /*
181 * We use a NULL nconf because GSSPROG has already been
182 * registered with rpcbind.
183 */
184 if (!svc_reg(transp, GSSPROG, GSSVERS, gssprog_1, NULL)) {
185 syslog(LOG_ERR,
186 gettext("unable to register "
187 "(GSSPROG, GSSVERS)"));
188 exit(1);
189 }
190
191 if (nconf)
192 freenetconfigent(nconf);
193 } else {
194 if (!gssd_debug)
195 daemonize_start();
196
197 openlog("gssd", LOG_PID, LOG_DAEMON);
198
199 if (svc_create_local_service(gssprog_1, GSSPROG, GSSVERS,
200 "netpath", "gssd") == 0) {
201 syslog(LOG_ERR, gettext("unable to create service"));
202 exit(1);
203 }
204
205 /* service created, now the daemon parent can exit */
206 daemonize_ready(0);
207 }
208
209
210 if (gssd_debug) {
211 fprintf(stderr,
212 gettext("gssd start: \n"));
213 }
214 svc_run();
215 abort();
216 /*NOTREACHED*/
217 #ifdef lint
218 return (1);
219 #endif
220 }
221
222 static void
usage(void)223 usage(void)
224 {
225 (void) fprintf(stderr, gettext("usage: gssd [-dg]\n"));
226 exit(1);
227 }
228
229
230 /*
231 * Fork, detach from tty, etc...
232 */
233 static int write_pipe_fd = -1;
234 static
235 void
daemonize_start()236 daemonize_start()
237 {
238 int pipe_fds[2];
239 unsigned char status = 1;
240
241 closefrom(0);
242
243 /* Open stdin/out/err, chdir, get a pipe */
244 if (open("/dev/null", O_RDONLY) < 0 ||
245 open("/dev/null", O_WRONLY) < 0 || dup(1) < 0 ||
246 chdir("/") < 0 || pipe(pipe_fds) < 0)
247 exit(1);
248
249 /* For daemonize_ready() */
250 write_pipe_fd = pipe_fds[1];
251
252 switch (fork()) {
253 case -1:
254 exit(1);
255 /* NOTREACHED */
256 case 0:
257 break;
258 default:
259 /* Wait for child to be ready befor exiting */
260 (void) close(pipe_fds[1]);
261 (void) signal(SIGPIPE, SIG_DFL);
262 (void) read(pipe_fds[0], &status, sizeof (status));
263 exit(status);
264 }
265
266 (void) close(pipe_fds[0]);
267 (void) setsid();
268 }
269
270 static
271 void
daemonize_ready(unsigned char status)272 daemonize_ready(unsigned char status)
273 {
274 if (write_pipe_fd == -1)
275 return;
276
277 (void) write(write_pipe_fd, &status, sizeof (status));
278 (void) close(write_pipe_fd);
279 write_pipe_fd = -1;
280 }
281
282 /*ARGSUSED*/
283 int
gssprog_1_freeresult(SVCXPRT * transport,xdrproc_t xdr_res,caddr_t res)284 gssprog_1_freeresult(SVCXPRT *transport, xdrproc_t xdr_res, caddr_t res)
285 {
286 xdr_free(xdr_res, res);
287 return (1);
288 }
289