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 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stropts.h>
31 #include <signal.h>
32 #include <fcntl.h>
33 #include <door.h>
34 #include <thread.h>
35 #include <priv_utils.h>
36 #include <locale.h>
37 #include <strings.h>
38 #include <syslog.h>
39 #include <unistd.h>
40 #include <nfs/nfs4.h>
41 #include <nfs/nfsid_map.h>
42 #include <rpcsvc/daemon_utils.h>
43 #include <arpa/nameser.h>
44 #include <nfs/nfssys.h>
45 #include <errno.h>
46 #include <pwd.h>
47 #include <grp.h>
48
49 extern struct group *_uncached_getgrgid_r(gid_t, struct group *, char *, int);
50 extern struct group *_uncached_getgrnam_r(const char *, struct group *,
51 char *, int);
52 extern struct passwd *_uncached_getpwuid_r(uid_t, struct passwd *, char *, int);
53 extern struct passwd *_uncached_getpwnam_r(const char *, struct passwd *,
54 char *, int);
55
56 /*
57 * seconds to cache nfsmapid domain info
58 */
59 #define NFSCFG_DEFAULT_DOMAIN_TMOUT (5 * 60)
60 #define NFSMAPID_DOOR "/var/run/nfsmapid_door"
61
62 extern void nfsmapid_func(void *, char *, size_t, door_desc_t *, uint_t);
63
64 extern void check_domain(int);
65 extern void idmap_kcall(int);
66 extern void open_diag_file(void);
67
68 size_t pwd_buflen = 0;
69 size_t grp_buflen = 0;
70 thread_t sig_thread;
71 static char *MyName;
72
73 /*
74 * nfscfg_domain_tmout is used by nfsv4-test scripts to query
75 * the nfsmapid daemon for the proper timeout. Don't delete !
76 */
77 time_t nfscfg_domain_tmout = NFSCFG_DEFAULT_DOMAIN_TMOUT;
78
79 /*
80 * Processing for daemonization
81 */
82 static void
daemonize(void)83 daemonize(void)
84 {
85 switch (fork()) {
86 case -1:
87 perror("nfsmapid: can't fork");
88 exit(2);
89 /* NOTREACHED */
90 case 0: /* child */
91 break;
92
93 default: /* parent */
94 _exit(0);
95 }
96
97 if (chdir("/") < 0)
98 syslog(LOG_ERR, gettext("chdir /: %m"));
99
100 /*
101 * Close stdin, stdout, and stderr.
102 * Open again to redirect input+output
103 */
104 (void) close(0);
105 (void) close(1);
106 (void) close(2);
107 (void) open("/dev/null", O_RDONLY);
108 (void) open("/dev/null", O_WRONLY);
109 (void) dup(1);
110 (void) setsid();
111 }
112
113 /* ARGSUSED */
114 static void *
sig_handler(void * arg)115 sig_handler(void *arg)
116 {
117 siginfo_t si;
118 sigset_t sigset;
119 struct timespec tmout;
120 int ret;
121
122 tmout.tv_nsec = 0;
123 (void) sigemptyset(&sigset);
124 (void) sigaddset(&sigset, SIGHUP);
125 (void) sigaddset(&sigset, SIGTERM);
126 #ifdef DEBUG
127 (void) sigaddset(&sigset, SIGINT);
128 #endif
129
130 /*CONSTCOND*/
131 while (1) {
132 tmout.tv_sec = nfscfg_domain_tmout;
133 if ((ret = sigtimedwait(&sigset, &si, &tmout)) != 0) {
134 /*
135 * EAGAIN: no signals arrived during timeout.
136 * check/update config files and continue.
137 */
138 if (ret == -1 && errno == EAGAIN) {
139 check_domain(0);
140 continue;
141 }
142
143 switch (si.si_signo) {
144 case SIGHUP:
145 check_domain(1);
146 break;
147 #ifdef DEBUG
148 case SIGINT:
149 exit(0);
150 #endif
151 case SIGTERM:
152 default:
153 exit(si.si_signo);
154 }
155 }
156 }
157 /*NOTREACHED*/
158 return (NULL);
159 }
160
161 /*
162 * Thread initialization. Mask out all signals we want our
163 * signal handler to handle for us from any other threads.
164 */
165 static void
thr_init(void)166 thr_init(void)
167 {
168 sigset_t sigset;
169 long thr_flags = (THR_NEW_LWP|THR_DAEMON|THR_SUSPENDED);
170
171 /*
172 * Before we kick off any other threads, mask out desired
173 * signals from main thread so that any subsequent threads
174 * don't receive said signals.
175 */
176 (void) thr_sigsetmask(NULL, NULL, &sigset);
177 (void) sigaddset(&sigset, SIGHUP);
178 (void) sigaddset(&sigset, SIGTERM);
179 #ifdef DEBUG
180 (void) sigaddset(&sigset, SIGINT);
181 #endif
182 (void) thr_sigsetmask(SIG_SETMASK, &sigset, NULL);
183
184 /*
185 * Create the signal handler thread suspended ! We do things
186 * this way at setup time to minimize the probability of
187 * introducing any race conditions _if_ the process were to
188 * get a SIGHUP signal while creating a new DNS query thread
189 * in get_dns_txt_domain().
190 */
191 if (thr_create(NULL, 0, sig_handler, 0, thr_flags, &sig_thread)) {
192 syslog(LOG_ERR,
193 gettext("Failed to create signal handling thread"));
194 exit(4);
195 }
196 }
197
198 static void
daemon_init(void)199 daemon_init(void)
200 {
201 struct passwd pwd;
202 struct group grp;
203 char *pwd_buf;
204 char *grp_buf;
205
206 /*
207 * passwd/group reentrant interfaces limits
208 */
209 pwd_buflen = (size_t)sysconf(_SC_GETPW_R_SIZE_MAX);
210 grp_buflen = (size_t)sysconf(_SC_GETGR_R_SIZE_MAX);
211
212 /*
213 * MT initialization is done first so that if there is the
214 * need to fire an additional thread to continue to query
215 * DNS, that thread is started off with the main thread's
216 * sigmask.
217 */
218 thr_init();
219
220 /*
221 * Determine nfsmapid domain.
222 */
223 check_domain(0);
224
225 /*
226 * In the case of nfsmapid running diskless, it is important
227 * to get the initial connections to the nameservices
228 * established to prevent problems like opening a devfs
229 * node to contact a nameservice being blocked by the
230 * resolution of an active devfs lookup.
231 * First issue a set*ent to "open" the databases and then
232 * get an entry and finally lookup a bogus entry to trigger
233 * any lazy opens.
234 */
235 setpwent();
236 setgrent();
237 (void) getpwent();
238 (void) getgrent();
239 if ((pwd_buf = malloc(pwd_buflen)) == NULL)
240 return;
241
242 (void) _uncached_getpwnam_r("NF21dmvP", &pwd, pwd_buf, pwd_buflen);
243 (void) _uncached_getpwuid_r(1181794, &pwd, pwd_buf, pwd_buflen);
244
245 if ((grp_buf = realloc(pwd_buf, grp_buflen)) == NULL) {
246 free(pwd_buf);
247 return;
248 }
249
250 (void) _uncached_getgrnam_r("NF21dmvP", &grp, grp_buf, grp_buflen);
251 (void) _uncached_getgrgid_r(1181794, &grp, grp_buf, grp_buflen);
252 free(grp_buf);
253 }
254
255 static int
start_svcs(void)256 start_svcs(void)
257 {
258 int doorfd = -1;
259 #ifdef DEBUG
260 int dfd;
261 #endif
262
263 if ((doorfd = door_create(nfsmapid_func, NULL,
264 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
265 syslog(LOG_ERR, "Unable to create door: %m\n");
266 return (1);
267 }
268
269 #ifdef DEBUG
270 /*
271 * Create a file system path for the door
272 */
273 if ((dfd = open(NFSMAPID_DOOR, O_RDWR|O_CREAT|O_TRUNC,
274 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
275 syslog(LOG_ERR, "Unable to open %s: %m\n", NFSMAPID_DOOR);
276 (void) close(doorfd);
277 return (1);
278 }
279
280 /*
281 * Clean up any stale associations
282 */
283 (void) fdetach(NFSMAPID_DOOR);
284
285 /*
286 * Register in namespace to pass to the kernel to door_ki_open
287 */
288 if (fattach(doorfd, NFSMAPID_DOOR) == -1) {
289 syslog(LOG_ERR, "Unable to fattach door: %m\n");
290 (void) close(dfd);
291 (void) close(doorfd);
292 return (1);
293 }
294 (void) close(dfd);
295 #endif
296
297 /*
298 * Now that we're actually running, go
299 * ahead and flush the kernel flushes
300 * Pass door name to kernel for door_ki_open
301 */
302 idmap_kcall(doorfd);
303
304 /*
305 * Wait for incoming calls
306 */
307 /*CONSTCOND*/
308 while (1)
309 (void) pause();
310
311 syslog(LOG_ERR, gettext("Door server exited"));
312 return (10);
313 }
314
315 /* ARGSUSED */
316 int
main(int argc,char ** argv)317 main(int argc, char **argv)
318 {
319 MyName = argv[0];
320
321 (void) setlocale(LC_ALL, "");
322 (void) textdomain(TEXT_DOMAIN);
323
324 /* _check_services() framework setup */
325 (void) _create_daemon_lock(NFSMAPID, DAEMON_UID, DAEMON_GID);
326
327 /*
328 * Open diag file in /var/run while we've got the perms
329 */
330 open_diag_file();
331
332 /*
333 * Initialize the daemon to basic + sys_nfs
334 */
335 #ifndef DEBUG
336 if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET,
337 DAEMON_UID, DAEMON_GID, PRIV_SYS_NFS, (char *)NULL) == -1) {
338 (void) fprintf(stderr, gettext("%s PRIV_SYS_NFS privilege "
339 "missing\n"), MyName);
340 exit(1);
341 }
342 #endif
343
344 /*
345 * Take away a subset of basic, while this is not the absolute
346 * minimum, it is important that it is unique among other
347 * daemons to insure that we get a unique cred that will
348 * result in a unique open_owner. If not, we run the risk
349 * of a diskless client deadlocking with a thread holding
350 * the open_owner seqid lock while upcalling the daemon.
351 * XXX This restriction will go away once we stop holding
352 * XXX open_owner lock across rfscalls!
353 */
354 (void) priv_set(PRIV_OFF, PRIV_PERMITTED,
355 PRIV_FILE_LINK_ANY, PRIV_PROC_SESSION,
356 (char *)NULL);
357
358 #ifndef DEBUG
359 daemonize();
360 switch (_enter_daemon_lock(NFSMAPID)) {
361 case 0:
362 break;
363
364 case -1:
365 syslog(LOG_ERR, "error locking for %s: %s", NFSMAPID,
366 strerror(errno));
367 exit(3);
368
369 default:
370 /* daemon was already running */
371 exit(0);
372 }
373 #endif
374 openlog(MyName, LOG_PID | LOG_NDELAY, LOG_DAEMON);
375
376 /* Initialize daemon subsystems */
377 daemon_init();
378
379 /* start services */
380 return (start_svcs());
381 }
382