Print this page
patch tsoome-feedback
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/fs.d/nfs/mountd/mountd.c
+++ new/usr/src/cmd/fs.d/nfs/mountd/mountd.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
24 24 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
25 25 */
26 26
27 27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 28 /* All Rights Reserved */
29 29
30 30 /*
31 31 * Portions of this source code were derived from Berkeley 4.3 BSD
32 32 * under license from the Regents of the University of California.
33 33 */
34 34
35 35 #include <stdio.h>
36 36 #include <stdio_ext.h>
37 37 #include <stdlib.h>
38 38 #include <ctype.h>
39 39 #include <sys/types.h>
40 40 #include <string.h>
41 41 #include <syslog.h>
42 42 #include <sys/param.h>
43 43 #include <rpc/rpc.h>
44 44 #include <sys/stat.h>
45 45 #include <netconfig.h>
46 46 #include <netdir.h>
47 47 #include <sys/file.h>
48 48 #include <sys/time.h>
49 49 #include <sys/errno.h>
50 50 #include <rpcsvc/mount.h>
51 51 #include <sys/pathconf.h>
52 52 #include <sys/systeminfo.h>
53 53 #include <sys/utsname.h>
54 54 #include <sys/wait.h>
55 55 #include <sys/resource.h>
56 56 #include <signal.h>
57 57 #include <locale.h>
58 58 #include <unistd.h>
59 59 #include <errno.h>
60 60 #include <sys/socket.h>
61 61 #include <netinet/in.h>
62 62 #include <arpa/inet.h>
63 63 #include <netdb.h>
64 64 #include <thread.h>
65 65 #include <assert.h>
66 66 #include <priv_utils.h>
67 67 #include <nfs/auth.h>
68 68 #include <nfs/nfssys.h>
69 69 #include <nfs/nfs.h>
70 70 #include <nfs/nfs_sec.h>
71 71 #include <rpcsvc/daemon_utils.h>
72 72 #include <deflt.h>
73 73 #include "../../fslib.h"
74 74 #include <sharefs/share.h>
75 75 #include <sharefs/sharetab.h>
76 76 #include "../lib/sharetab.h"
77 77 #include "mountd.h"
78 78 #include <tsol/label.h>
79 79 #include <sys/tsol/label_macro.h>
80 80 #include <libtsnet.h>
81 81 #include <sys/sdt.h>
82 82 #include <libscf.h>
83 83 #include <limits.h>
84 84 #include <sys/nvpair.h>
85 85 #include <attr.h>
86 86 #include "smfcfg.h"
87 87 #include <pwd.h>
88 88 #include <grp.h>
89 89 #include <alloca.h>
90 90
91 91 extern int daemonize_init(void);
92 92 extern void daemonize_fini(int);
93 93
94 94 extern int _nfssys(int, void *);
95 95
96 96 struct sh_list *share_list;
97 97
98 98 rwlock_t sharetab_lock; /* lock to protect the cached sharetab */
99 99 static mutex_t mnttab_lock; /* prevent concurrent mnttab readers */
100 100
101 101 static mutex_t logging_queue_lock;
102 102 static cond_t logging_queue_cv;
103 103
104 104 static share_t *find_lofsentry(char *, int *);
105 105 static int getclientsflavors_old(share_t *, struct cln *, int *);
106 106 static int getclientsflavors_new(share_t *, struct cln *, int *);
107 107 static int check_client_old(share_t *, struct cln *, int, uid_t, gid_t, uint_t,
108 108 gid_t *, uid_t *, gid_t *, uint_t *, gid_t **);
109 109 static int check_client_new(share_t *, struct cln *, int, uid_t, gid_t, uint_t,
110 110 gid_t *, uid_t *, gid_t *i, uint_t *, gid_t **);
111 111 static void mnt(struct svc_req *, SVCXPRT *);
112 112 static void mnt_pathconf(struct svc_req *);
113 113 static int mount(struct svc_req *r);
114 114 static void sh_free(struct sh_list *);
115 115 static void umount(struct svc_req *);
116 116 static void umountall(struct svc_req *);
117 117 static int newopts(char *);
118 118 static tsol_tpent_t *get_client_template(struct sockaddr *);
119 119
120 120 static int verbose;
121 121 static int rejecting;
122 122 static int mount_vers_min = MOUNTVERS;
123 123 static int mount_vers_max = MOUNTVERS3;
124 124
125 125 extern void nfscmd_func(void *, char *, size_t, door_desc_t *, uint_t);
126 126
127 127 thread_t nfsauth_thread;
128 128 thread_t cmd_thread;
129 129 thread_t logging_thread;
130 130
131 131 typedef struct logging_data {
132 132 char *ld_host;
133 133 char *ld_path;
134 134 char *ld_rpath;
135 135 int ld_status;
136 136 char *ld_netid;
137 137 struct netbuf *ld_nb;
138 138 struct logging_data *ld_next;
139 139 } logging_data;
140 140
141 141 static logging_data *logging_head = NULL;
142 142 static logging_data *logging_tail = NULL;
143 143
144 144 /*
145 145 * Our copy of some system variables obtained using sysconf(3c)
146 146 */
147 147 static long ngroups_max; /* _SC_NGROUPS_MAX */
148 148 static long pw_size; /* _SC_GETPW_R_SIZE_MAX */
149 149
150 150 /* ARGSUSED */
151 151 static void *
152 152 nfsauth_svc(void *arg)
153 153 {
154 154 int doorfd = -1;
155 155 uint_t darg;
156 156 #ifdef DEBUG
157 157 int dfd;
158 158 #endif
159 159
160 160 if ((doorfd = door_create(nfsauth_func, NULL,
161 161 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
162 162 syslog(LOG_ERR, "Unable to create door: %m\n");
163 163 exit(10);
164 164 }
165 165
166 166 #ifdef DEBUG
167 167 /*
168 168 * Create a file system path for the door
169 169 */
170 170 if ((dfd = open(MOUNTD_DOOR, O_RDWR|O_CREAT|O_TRUNC,
171 171 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
172 172 syslog(LOG_ERR, "Unable to open %s: %m\n", MOUNTD_DOOR);
173 173 (void) close(doorfd);
174 174 exit(11);
175 175 }
176 176
177 177 /*
178 178 * Clean up any stale namespace associations
179 179 */
180 180 (void) fdetach(MOUNTD_DOOR);
181 181
182 182 /*
183 183 * Register in namespace to pass to the kernel to door_ki_open
184 184 */
185 185 if (fattach(doorfd, MOUNTD_DOOR) == -1) {
186 186 syslog(LOG_ERR, "Unable to fattach door: %m\n");
187 187 (void) close(dfd);
188 188 (void) close(doorfd);
189 189 exit(12);
190 190 }
191 191 (void) close(dfd);
192 192 #endif
193 193
194 194 /*
195 195 * Must pass the doorfd down to the kernel.
196 196 */
197 197 darg = doorfd;
198 198 (void) _nfssys(MOUNTD_ARGS, &darg);
199 199
200 200 /*
201 201 * Wait for incoming calls
202 202 */
203 203 /*CONSTCOND*/
204 204 for (;;)
205 205 (void) pause();
206 206
207 207 /*NOTREACHED*/
208 208 syslog(LOG_ERR, gettext("Door server exited"));
209 209 return (NULL);
210 210 }
211 211
212 212 /*
213 213 * NFS command service thread code for setup and handling of the
214 214 * nfs_cmd requests for character set conversion and other future
215 215 * events.
216 216 */
217 217
218 218 static void *
219 219 cmd_svc(void *arg)
220 220 {
221 221 int doorfd = -1;
222 222 uint_t darg;
223 223
224 224 if ((doorfd = door_create(nfscmd_func, NULL,
225 225 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
226 226 syslog(LOG_ERR, "Unable to create cmd door: %m\n");
227 227 exit(10);
228 228 }
229 229
230 230 /*
231 231 * Must pass the doorfd down to the kernel.
232 232 */
233 233 darg = doorfd;
234 234 (void) _nfssys(NFSCMD_ARGS, &darg);
235 235
236 236 /*
237 237 * Wait for incoming calls
238 238 */
239 239 /*CONSTCOND*/
240 240 for (;;)
241 241 (void) pause();
242 242
243 243 /*NOTREACHED*/
244 244 syslog(LOG_ERR, gettext("Cmd door server exited"));
245 245 return (NULL);
246 246 }
247 247
248 248 static void
249 249 free_logging_data(logging_data *lq)
250 250 {
251 251 if (lq != NULL) {
252 252 free(lq->ld_host);
253 253 free(lq->ld_netid);
254 254
255 255 if (lq->ld_nb != NULL) {
256 256 free(lq->ld_nb->buf);
257 257 free(lq->ld_nb);
258 258 }
259 259
260 260 free(lq->ld_path);
261 261 free(lq->ld_rpath);
262 262
263 263 free(lq);
264 264 }
265 265 }
266 266
267 267 static logging_data *
268 268 remove_head_of_queue(void)
269 269 {
270 270 logging_data *lq;
271 271
272 272 /*
273 273 * Pull it off the queue.
274 274 */
275 275 lq = logging_head;
276 276 if (lq) {
277 277 logging_head = lq->ld_next;
278 278
279 279 /*
280 280 * Drained it.
281 281 */
282 282 if (logging_head == NULL) {
283 283 logging_tail = NULL;
284 284 }
285 285 }
286 286
287 287 return (lq);
288 288 }
289 289
290 290 static void
291 291 do_logging_queue(logging_data *lq)
292 292 {
293 293 int cleared = 0;
294 294 char *host;
295 295
296 296 while (lq) {
297 297 struct cln cln;
298 298
299 299 if (lq->ld_host == NULL) {
300 300 DTRACE_PROBE(mountd, name_by_lazy);
301 301 cln_init_lazy(&cln, lq->ld_netid, lq->ld_nb);
302 302 host = cln_gethost(&cln);
303 303 } else
304 304 host = lq->ld_host;
305 305
306 306 audit_mountd_mount(host, lq->ld_path, lq->ld_status); /* BSM */
307 307
308 308 /* add entry to mount list */
309 309 if (lq->ld_rpath)
310 310 mntlist_new(host, lq->ld_rpath);
311 311
312 312 if (lq->ld_host == NULL)
313 313 cln_fini(&cln);
314 314
315 315 free_logging_data(lq);
316 316 cleared++;
317 317
318 318 (void) mutex_lock(&logging_queue_lock);
319 319 lq = remove_head_of_queue();
320 320 (void) mutex_unlock(&logging_queue_lock);
321 321 }
322 322
323 323 DTRACE_PROBE1(mountd, logging_cleared, cleared);
324 324 }
325 325
326 326 static void *
327 327 logging_svc(void *arg)
328 328 {
329 329 logging_data *lq;
330 330
331 331 for (;;) {
332 332 (void) mutex_lock(&logging_queue_lock);
333 333 while (logging_head == NULL) {
334 334 (void) cond_wait(&logging_queue_cv,
335 335 &logging_queue_lock);
336 336 }
337 337
338 338 lq = remove_head_of_queue();
339 339 (void) mutex_unlock(&logging_queue_lock);
340 340
341 341 do_logging_queue(lq);
342 342 }
343 343
344 344 /*NOTREACHED*/
345 345 syslog(LOG_ERR, gettext("Logging server exited"));
346 346 return (NULL);
347 347 }
348 348
349 349 static int
350 350 convert_int(int *val, char *str)
351 351 {
352 352 long lval;
353 353
354 354 if (str == NULL || !isdigit(*str))
355 355 return (-1);
356 356
357 357 lval = strtol(str, &str, 10);
358 358 if (*str != '\0' || lval > INT_MAX)
359 359 return (-2);
360 360
361 361 *val = (int)lval;
362 362 return (0);
363 363 }
364 364
365 365 int
366 366 main(int argc, char *argv[])
367 367 {
368 368 int pid;
369 369 int c;
370 370 int rpc_svc_fdunlim = 1;
371 371 int rpc_svc_mode = RPC_SVC_MT_AUTO;
372 372 int maxrecsz = RPC_MAXDATASIZE;
373 373 bool_t exclbind = TRUE;
374 374 bool_t can_do_mlp;
375 375 long thr_flags = (THR_NEW_LWP|THR_DAEMON);
376 376 char defval[4];
377 377 int defvers, ret, bufsz;
378 378 struct rlimit rl;
379 379 int listen_backlog = 0;
380 380 int max_threads = 0;
381 381 int tmp;
382 382
383 383 int pipe_fd = -1;
384 384
385 385 /*
386 386 * Mountd requires uid 0 for:
387 387 * /etc/rmtab updates (we could chown it to daemon)
388 388 * /etc/dfs/dfstab reading (it wants to lock out share which
389 389 * doesn't do any locking before first truncate;
390 390 * NFS share does; should use fcntl locking instead)
391 391 * Needed privileges:
392 392 * auditing
393 393 * nfs syscall
394 394 * file dac search (so it can stat all files)
395 395 * Optional privileges:
396 396 * MLP
397 397 */
398 398 can_do_mlp = priv_ineffect(PRIV_NET_BINDMLP);
399 399 if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, -1, -1,
400 400 PRIV_SYS_NFS, PRIV_PROC_AUDIT, PRIV_FILE_DAC_SEARCH,
401 401 can_do_mlp ? PRIV_NET_BINDMLP : NULL, NULL) == -1) {
402 402 (void) fprintf(stderr,
403 403 "%s: must be run with sufficient privileges\n",
404 404 argv[0]);
405 405 exit(1);
406 406 }
407 407
408 408 if (getrlimit(RLIMIT_NOFILE, &rl) != 0) {
409 409 syslog(LOG_ERR, "getrlimit failed");
410 410 } else {
411 411 rl.rlim_cur = rl.rlim_max;
412 412 if (setrlimit(RLIMIT_NOFILE, &rl) != 0)
413 413 syslog(LOG_ERR, "setrlimit failed");
414 414 }
415 415
416 416 (void) enable_extended_FILE_stdio(-1, -1);
417 417
418 418 ret = nfs_smf_get_iprop("mountd_max_threads", &max_threads,
419 419 DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
420 420 if (ret != SA_OK) {
421 421 syslog(LOG_ERR, "Reading of mountd_max_threads from SMF "
422 422 "failed, using default value");
423 423 }
424 424
425 425 while ((c = getopt(argc, argv, "vrm:")) != EOF) {
426 426 switch (c) {
427 427 case 'v':
428 428 verbose++;
429 429 break;
430 430 case 'r':
431 431 rejecting = 1;
432 432 break;
433 433 case 'm':
434 434 if (convert_int(&tmp, optarg) != 0 || tmp < 1) {
435 435 (void) fprintf(stderr, "%s: invalid "
436 436 "max_threads option, using defaults\n",
437 437 argv[0]);
438 438 break;
439 439 }
440 440 max_threads = tmp;
441 441 break;
442 442 default:
443 443 fprintf(stderr, "usage: mountd [-v] [-r]\n");
444 444 exit(1);
445 445 }
446 446 }
447 447
448 448 /*
449 449 * Read in the NFS version values from config file.
450 450 */
451 451 bufsz = 4;
452 452 ret = nfs_smf_get_prop("server_versmin", defval, DEFAULT_INSTANCE,
453 453 SCF_TYPE_INTEGER, NFSD, &bufsz);
454 454 if (ret == SA_OK) {
455 455 errno = 0;
456 456 defvers = strtol(defval, (char **)NULL, 10);
457 457 if (errno == 0) {
458 458 mount_vers_min = defvers;
459 459 /*
460 460 * special because NFSv2 is
461 461 * supported by mount v1 & v2
462 462 */
463 463 if (defvers == NFS_VERSION)
464 464 mount_vers_min = MOUNTVERS;
465 465 }
466 466 }
467 467
468 468 bufsz = 4;
469 469 ret = nfs_smf_get_prop("server_versmax", defval, DEFAULT_INSTANCE,
470 470 SCF_TYPE_INTEGER, NFSD, &bufsz);
471 471 if (ret == SA_OK) {
472 472 errno = 0;
473 473 defvers = strtol(defval, (char **)NULL, 10);
474 474 if (errno == 0) {
475 475 mount_vers_max = defvers;
476 476 }
477 477 }
478 478
479 479 ret = nfs_smf_get_iprop("mountd_listen_backlog", &listen_backlog,
480 480 DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
481 481 if (ret != SA_OK) {
482 482 syslog(LOG_ERR, "Reading of mountd_listen_backlog from SMF "
483 483 "failed, using default value");
484 484 }
485 485
486 486 /*
487 487 * Sanity check versions,
488 488 * even though we may get versions > MOUNTVERS3, we still need
489 489 * to start nfsauth service, so continue on regardless of values.
490 490 */
491 491 if (mount_vers_min > mount_vers_max) {
492 492 fprintf(stderr, "server_versmin > server_versmax\n");
493 493 mount_vers_max = mount_vers_min;
494 494 }
495 495 (void) setlocale(LC_ALL, "");
496 496 (void) rwlock_init(&sharetab_lock, USYNC_THREAD, NULL);
497 497 (void) mutex_init(&mnttab_lock, USYNC_THREAD, NULL);
498 498 (void) mutex_init(&logging_queue_lock, USYNC_THREAD, NULL);
499 499 (void) cond_init(&logging_queue_cv, USYNC_THREAD, NULL);
500 500
501 501 netgroup_init();
502 502
503 503 #if !defined(TEXT_DOMAIN)
504 504 #define TEXT_DOMAIN "SYS_TEST"
505 505 #endif
506 506 (void) textdomain(TEXT_DOMAIN);
507 507
508 508 /* Don't drop core if the NFS module isn't loaded. */
509 509 (void) signal(SIGSYS, SIG_IGN);
510 510
511 511 pipe_fd = daemonize_init();
512 512
513 513 /*
514 514 * If we coredump it'll be in /core
515 515 */
516 516 if (chdir("/") < 0)
517 517 fprintf(stderr, "chdir /: %s\n", strerror(errno));
518 518
519 519 openlog("mountd", LOG_PID, LOG_DAEMON);
520 520
521 521 /*
522 522 * establish our lock on the lock file and write our pid to it.
523 523 * exit if some other process holds the lock, or if there's any
524 524 * error in writing/locking the file.
525 525 */
526 526 pid = _enter_daemon_lock(MOUNTD);
527 527 switch (pid) {
528 528 case 0:
529 529 break;
530 530 case -1:
531 531 fprintf(stderr, "error locking for %s: %s\n", MOUNTD,
532 532 strerror(errno));
533 533 exit(2);
534 534 default:
535 535 /* daemon was already running */
536 536 exit(0);
537 537 }
538 538
539 539 audit_mountd_setup(); /* BSM */
540 540
541 541 /*
542 542 * Get required system variables
543 543 */
544 544 if ((ngroups_max = sysconf(_SC_NGROUPS_MAX)) == -1) {
545 545 syslog(LOG_ERR, "Unable to get _SC_NGROUPS_MAX");
546 546 exit(1);
547 547 }
548 548 if ((pw_size = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) {
549 549 syslog(LOG_ERR, "Unable to get _SC_GETPW_R_SIZE_MAX");
550 550 exit(1);
551 551 }
552 552
553 553 /*
554 554 * Set number of file descriptors to unlimited
555 555 */
556 556 if (!rpc_control(RPC_SVC_USE_POLLFD, &rpc_svc_fdunlim)) {
557 557 syslog(LOG_INFO, "unable to set number of FDs to unlimited");
558 558 }
559 559
560 560 /*
561 561 * Tell RPC that we want automatic thread mode.
562 562 * A new thread will be spawned for each request.
563 563 */
564 564 if (!rpc_control(RPC_SVC_MTMODE_SET, &rpc_svc_mode)) {
565 565 fprintf(stderr, "unable to set automatic MT mode\n");
566 566 exit(1);
567 567 }
568 568
569 569 /*
570 570 * Enable non-blocking mode and maximum record size checks for
571 571 * connection oriented transports.
572 572 */
573 573 if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrecsz)) {
574 574 fprintf(stderr, "unable to set RPC max record size\n");
575 575 }
576 576
577 577 /*
578 578 * Prevent our non-priv udp and tcp ports bound w/wildcard addr
579 579 * from being hijacked by a bind to a more specific addr.
580 580 */
581 581 if (!rpc_control(__RPC_SVC_EXCLBIND_SET, &exclbind)) {
582 582 fprintf(stderr, "warning: unable to set udp/tcp EXCLBIND\n");
583 583 }
584 584
585 585 /*
586 586 * Set the maximum number of outstanding connection
587 587 * indications (listen backlog) to the value specified.
588 588 */
589 589 if (listen_backlog > 0 && !rpc_control(__RPC_SVC_LSTNBKLOG_SET,
590 590 &listen_backlog)) {
591 591 fprintf(stderr, "unable to set listen backlog\n");
592 592 exit(1);
593 593 }
594 594
595 595 /*
596 596 * If max_threads was specified, then set the
597 597 * maximum number of threads to the value specified.
598 598 */
599 599 if (max_threads > 0 && !rpc_control(RPC_SVC_THRMAX_SET, &max_threads)) {
600 600 fprintf(stderr, "unable to set max_threads\n");
601 601 exit(1);
602 602 }
603 603
604 604 /*
605 605 * Make sure to unregister any previous versions in case the
606 606 * user is reconfiguring the server in interesting ways.
607 607 */
608 608 svc_unreg(MOUNTPROG, MOUNTVERS);
609 609 svc_unreg(MOUNTPROG, MOUNTVERS_POSIX);
610 610 svc_unreg(MOUNTPROG, MOUNTVERS3);
611 611
612 612 /*
613 613 * Create the nfsauth thread with same signal disposition
614 614 * as the main thread. We need to create a separate thread
615 615 * since mountd() will be both an RPC server (for remote
616 616 * traffic) _and_ a doors server (for kernel upcalls).
617 617 */
618 618 if (thr_create(NULL, 0, nfsauth_svc, 0, thr_flags, &nfsauth_thread)) {
619 619 fprintf(stderr,
620 620 gettext("Failed to create NFSAUTH svc thread\n"));
621 621 exit(2);
622 622 }
623 623
624 624 /*
625 625 * Create the cmd service thread with same signal disposition
626 626 * as the main thread. We need to create a separate thread
627 627 * since mountd() will be both an RPC server (for remote
628 628 * traffic) _and_ a doors server (for kernel upcalls).
629 629 */
630 630 if (thr_create(NULL, 0, cmd_svc, 0, thr_flags, &cmd_thread)) {
631 631 syslog(LOG_ERR, gettext("Failed to create CMD svc thread"));
632 632 exit(2);
633 633 }
634 634
635 635 /*
636 636 * Create an additional thread to service the rmtab and
637 637 * audit_mountd_mount logging for mount requests. Use the same
638 638 * signal disposition as the main thread. We create
639 639 * a separate thread to allow the mount request threads to
640 640 * clear as soon as possible.
641 641 */
642 642 if (thr_create(NULL, 0, logging_svc, 0, thr_flags, &logging_thread)) {
643 643 syslog(LOG_ERR, gettext("Failed to create LOGGING svc thread"));
644 644 exit(2);
645 645 }
646 646
647 647 /*
648 648 * Create datagram and connection oriented services
649 649 */
650 650 if (mount_vers_max >= MOUNTVERS) {
651 651 if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "datagram_v") == 0) {
652 652 fprintf(stderr,
653 653 "couldn't register datagram_v MOUNTVERS\n");
654 654 exit(1);
655 655 }
656 656 if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "circuit_v") == 0) {
657 657 fprintf(stderr,
658 658 "couldn't register circuit_v MOUNTVERS\n");
659 659 exit(1);
660 660 }
661 661 }
662 662
663 663 if (mount_vers_max >= MOUNTVERS_POSIX) {
664 664 if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX,
665 665 "datagram_v") == 0) {
666 666 fprintf(stderr,
667 667 "couldn't register datagram_v MOUNTVERS_POSIX\n");
668 668 exit(1);
669 669 }
670 670 if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX,
671 671 "circuit_v") == 0) {
672 672 fprintf(stderr,
673 673 "couldn't register circuit_v MOUNTVERS_POSIX\n");
674 674 exit(1);
675 675 }
676 676 }
677 677
678 678 if (mount_vers_max >= MOUNTVERS3) {
679 679 if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "datagram_v") == 0) {
680 680 fprintf(stderr,
681 681 "couldn't register datagram_v MOUNTVERS3\n");
682 682 exit(1);
683 683 }
684 684 if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "circuit_v") == 0) {
685 685 fprintf(stderr,
686 686 "couldn't register circuit_v MOUNTVERS3\n");
687 687 exit(1);
688 688 }
689 689 }
690 690
691 691 /*
692 692 * Start serving
693 693 */
694 694 rmtab_load();
695 695
696 696 daemonize_fini(pipe_fd);
697 697
698 698 /* Get rid of the most dangerous basic privileges. */
699 699 __fini_daemon_priv(PRIV_PROC_EXEC, PRIV_PROC_INFO, PRIV_PROC_SESSION,
700 700 (char *)NULL);
701 701
702 702 svc_run();
703 703 syslog(LOG_ERR, "Error: svc_run shouldn't have returned");
704 704 abort();
705 705
706 706 /* NOTREACHED */
707 707 return (0);
708 708 }
709 709
710 710 /*
711 711 * Server procedure switch routine
712 712 */
713 713 void
714 714 mnt(struct svc_req *rqstp, SVCXPRT *transp)
715 715 {
716 716 switch (rqstp->rq_proc) {
717 717 case NULLPROC:
718 718 errno = 0;
719 719 if (!svc_sendreply(transp, xdr_void, (char *)0))
720 720 log_cant_reply(transp);
721 721 return;
722 722
723 723 case MOUNTPROC_MNT:
724 724 (void) mount(rqstp);
725 725 return;
726 726
727 727 case MOUNTPROC_DUMP:
728 728 mntlist_send(transp);
729 729 return;
730 730
731 731 case MOUNTPROC_UMNT:
732 732 umount(rqstp);
733 733 return;
734 734
735 735 case MOUNTPROC_UMNTALL:
736 736 umountall(rqstp);
737 737 return;
738 738
739 739 case MOUNTPROC_EXPORT:
740 740 case MOUNTPROC_EXPORTALL:
741 741 export(rqstp);
742 742 return;
743 743
744 744 case MOUNTPROC_PATHCONF:
745 745 if (rqstp->rq_vers == MOUNTVERS_POSIX)
746 746 mnt_pathconf(rqstp);
747 747 else
748 748 svcerr_noproc(transp);
749 749 return;
750 750
751 751 default:
752 752 svcerr_noproc(transp);
753 753 return;
754 754 }
755 755 }
756 756
757 757 void
758 758 log_cant_reply_cln(struct cln *cln)
759 759 {
760 760 int saverrno;
761 761 char *host;
762 762
763 763 saverrno = errno; /* save error code */
764 764
765 765 host = cln_gethost(cln);
766 766 if (host == NULL)
767 767 return;
768 768
769 769 errno = saverrno;
770 770 if (errno == 0)
771 771 syslog(LOG_ERR, "couldn't send reply to %s", host);
772 772 else
773 773 syslog(LOG_ERR, "couldn't send reply to %s: %m", host);
774 774 }
775 775
776 776 void
777 777 log_cant_reply(SVCXPRT *transp)
778 778 {
779 779 int saverrno;
780 780 struct cln cln;
781 781
782 782 saverrno = errno; /* save error code */
783 783 cln_init(&cln, transp);
784 784 errno = saverrno;
785 785
786 786 log_cant_reply_cln(&cln);
787 787
788 788 cln_fini(&cln);
789 789 }
790 790
791 791 /*
792 792 * Answer pathconf questions for the mount point fs
793 793 */
794 794 static void
795 795 mnt_pathconf(struct svc_req *rqstp)
796 796 {
797 797 SVCXPRT *transp;
798 798 struct pathcnf p;
799 799 char *path, rpath[MAXPATHLEN];
800 800 struct stat st;
801 801
802 802 transp = rqstp->rq_xprt;
803 803 path = NULL;
804 804 (void) memset((caddr_t)&p, 0, sizeof (p));
805 805
806 806 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
807 807 svcerr_decode(transp);
808 808 return;
809 809 }
810 810 if (lstat(path, &st) < 0) {
811 811 _PC_SET(_PC_ERROR, p.pc_mask);
812 812 goto done;
813 813 }
814 814 /*
815 815 * Get a path without symbolic links.
816 816 */
817 817 if (realpath(path, rpath) == NULL) {
818 818 syslog(LOG_DEBUG,
819 819 "mount request: realpath failed on %s: %m",
820 820 path);
821 821 _PC_SET(_PC_ERROR, p.pc_mask);
822 822 goto done;
823 823 }
824 824 (void) memset((caddr_t)&p, 0, sizeof (p));
825 825 /*
826 826 * can't ask about devices over NFS
827 827 */
828 828 _PC_SET(_PC_MAX_CANON, p.pc_mask);
829 829 _PC_SET(_PC_MAX_INPUT, p.pc_mask);
830 830 _PC_SET(_PC_PIPE_BUF, p.pc_mask);
831 831 _PC_SET(_PC_VDISABLE, p.pc_mask);
832 832
833 833 errno = 0;
834 834 p.pc_link_max = pathconf(rpath, _PC_LINK_MAX);
835 835 if (errno)
836 836 _PC_SET(_PC_LINK_MAX, p.pc_mask);
837 837 p.pc_name_max = pathconf(rpath, _PC_NAME_MAX);
838 838 if (errno)
839 839 _PC_SET(_PC_NAME_MAX, p.pc_mask);
840 840 p.pc_path_max = pathconf(rpath, _PC_PATH_MAX);
841 841 if (errno)
842 842 _PC_SET(_PC_PATH_MAX, p.pc_mask);
843 843 if (pathconf(rpath, _PC_NO_TRUNC) == 1)
844 844 _PC_SET(_PC_NO_TRUNC, p.pc_mask);
845 845 if (pathconf(rpath, _PC_CHOWN_RESTRICTED) == 1)
846 846 _PC_SET(_PC_CHOWN_RESTRICTED, p.pc_mask);
847 847
848 848 done:
849 849 errno = 0;
850 850 if (!svc_sendreply(transp, xdr_ppathcnf, (char *)&p))
851 851 log_cant_reply(transp);
852 852 if (path != NULL)
853 853 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
854 854 }
855 855
856 856 /*
857 857 * If the rootmount (export) option is specified, the all mount requests for
858 858 * subdirectories return EACCES.
859 859 */
860 860 static int
861 861 checkrootmount(share_t *sh, char *rpath)
862 862 {
863 863 char *val;
864 864
865 865 if ((val = getshareopt(sh->sh_opts, SHOPT_NOSUB)) != NULL) {
866 866 free(val);
867 867 if (strcmp(sh->sh_path, rpath) != 0)
868 868 return (0);
869 869 else
870 870 return (1);
871 871 } else
872 872 return (1);
873 873 }
874 874
875 875 #define MAX_FLAVORS 128
876 876
877 877 /*
878 878 * Return only EACCES if client does not have access
879 879 * to this directory.
880 880 * "If the server exports only /a/b, an attempt to
881 881 * mount a/b/c will fail with ENOENT if the directory
882 882 * does not exist"... However, if the client
883 883 * does not have access to /a/b, an attacker can
884 884 * determine whether the directory exists.
885 885 * This routine checks either existence of the file or
886 886 * existence of the file name entry in the mount table.
887 887 * If the file exists and there is no file name entry,
888 888 * the error returned should be EACCES.
889 889 * If the file does not exist, it must be determined
890 890 * whether the client has access to a parent
891 891 * directory. If the client has access to a parent
892 892 * directory, the error returned should be ENOENT,
893 893 * otherwise EACCES.
894 894 */
895 895 static int
896 896 mount_enoent_error(struct cln *cln, char *path, char *rpath, int *flavor_list)
897 897 {
898 898 char *checkpath, *dp;
899 899 share_t *sh = NULL;
900 900 int realpath_error = ENOENT, reply_error = EACCES, lofs_tried = 0;
901 901 int flavor_count;
902 902
903 903 checkpath = strdup(path);
904 904 if (checkpath == NULL) {
905 905 syslog(LOG_ERR, "mount_enoent: no memory");
906 906 return (EACCES);
907 907 }
908 908
909 909 /* CONSTCOND */
910 910 while (1) {
911 911 if (sh) {
912 912 sharefree(sh);
913 913 sh = NULL;
914 914 }
915 915
916 916 if ((sh = findentry(rpath)) == NULL &&
917 917 (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
918 918 /*
919 919 * There is no file name entry.
920 920 * If the file (with symbolic links resolved) exists,
921 921 * the error returned should be EACCES.
922 922 */
923 923 if (realpath_error == 0)
924 924 break;
925 925 } else if (checkrootmount(sh, rpath) == 0) {
926 926 /*
927 927 * This is a "nosub" only export, in which case,
928 928 * mounting subdirectories isn't allowed.
929 929 * If the file (with symbolic links resolved) exists,
930 930 * the error returned should be EACCES.
931 931 */
932 932 if (realpath_error == 0)
933 933 break;
934 934 } else {
935 935 /*
936 936 * Check permissions in mount table.
937 937 */
938 938 if (newopts(sh->sh_opts))
939 939 flavor_count = getclientsflavors_new(sh, cln,
940 940 flavor_list);
941 941 else
942 942 flavor_count = getclientsflavors_old(sh, cln,
943 943 flavor_list);
944 944 if (flavor_count != 0) {
945 945 /*
946 946 * Found entry in table and
947 947 * client has correct permissions.
948 948 */
949 949 reply_error = ENOENT;
950 950 break;
951 951 }
952 952 }
953 953
954 954 /*
955 955 * Check all parent directories.
956 956 */
957 957 dp = strrchr(checkpath, '/');
958 958 if (dp == NULL)
959 959 break;
960 960 *dp = '\0';
961 961 if (strlen(checkpath) == 0)
962 962 break;
963 963 /*
964 964 * Get the real path (no symbolic links in it)
965 965 */
966 966 if (realpath(checkpath, rpath) == NULL) {
967 967 if (errno != ENOENT)
968 968 break;
969 969 } else {
970 970 realpath_error = 0;
971 971 }
972 972 }
973 973
974 974 if (sh)
975 975 sharefree(sh);
976 976 free(checkpath);
977 977 return (reply_error);
978 978 }
979 979
980 980 /*
981 981 * We need to inform the caller whether or not we were
982 982 * able to add a node to the queue. If we are not, then
983 983 * it is up to the caller to go ahead and log the data.
984 984 */
985 985 static int
986 986 enqueue_logging_data(char *host, SVCXPRT *transp, char *path,
987 987 char *rpath, int status, int error)
988 988 {
989 989 logging_data *lq;
990 990 struct netbuf *nb;
991 991
992 992 lq = (logging_data *)calloc(1, sizeof (logging_data));
993 993 if (lq == NULL)
994 994 goto cleanup;
995 995
996 996 /*
997 997 * We might not yet have the host...
998 998 */
999 999 if (host) {
1000 1000 DTRACE_PROBE1(mountd, log_host, host);
1001 1001 lq->ld_host = strdup(host);
1002 1002 if (lq->ld_host == NULL)
1003 1003 goto cleanup;
1004 1004 } else {
1005 1005 DTRACE_PROBE(mountd, log_no_host);
1006 1006
1007 1007 lq->ld_netid = strdup(transp->xp_netid);
1008 1008 if (lq->ld_netid == NULL)
1009 1009 goto cleanup;
1010 1010
1011 1011 lq->ld_nb = calloc(1, sizeof (struct netbuf));
1012 1012 if (lq->ld_nb == NULL)
1013 1013 goto cleanup;
1014 1014
1015 1015 nb = svc_getrpccaller(transp);
1016 1016 if (nb == NULL) {
1017 1017 DTRACE_PROBE(mountd, e__nb__enqueue);
1018 1018 goto cleanup;
1019 1019 }
1020 1020
1021 1021 DTRACE_PROBE(mountd, nb_set_enqueue);
1022 1022
1023 1023 lq->ld_nb->maxlen = nb->maxlen;
1024 1024 lq->ld_nb->len = nb->len;
1025 1025
1026 1026 lq->ld_nb->buf = malloc(lq->ld_nb->len);
1027 1027 if (lq->ld_nb->buf == NULL)
1028 1028 goto cleanup;
1029 1029
1030 1030 bcopy(nb->buf, lq->ld_nb->buf, lq->ld_nb->len);
1031 1031 }
1032 1032
1033 1033 lq->ld_path = strdup(path);
1034 1034 if (lq->ld_path == NULL)
1035 1035 goto cleanup;
1036 1036
1037 1037 if (!error) {
1038 1038 lq->ld_rpath = strdup(rpath);
1039 1039 if (lq->ld_rpath == NULL)
1040 1040 goto cleanup;
1041 1041 }
1042 1042
1043 1043 lq->ld_status = status;
1044 1044
1045 1045 /*
1046 1046 * Add to the tail of the logging queue.
1047 1047 */
1048 1048 (void) mutex_lock(&logging_queue_lock);
1049 1049 if (logging_tail == NULL) {
1050 1050 logging_tail = logging_head = lq;
1051 1051 } else {
1052 1052 logging_tail->ld_next = lq;
1053 1053 logging_tail = lq;
1054 1054 }
1055 1055 (void) cond_signal(&logging_queue_cv);
1056 1056 (void) mutex_unlock(&logging_queue_lock);
1057 1057
1058 1058 return (TRUE);
1059 1059
1060 1060 cleanup:
1061 1061
1062 1062 free_logging_data(lq);
1063 1063
1064 1064 return (FALSE);
1065 1065 }
1066 1066
1067 1067
1068 1068 #define CLN_CLNAMES (1 << 0)
1069 1069 #define CLN_HOST (1 << 1)
1070 1070
1071 1071 static void
1072 1072 cln_init_common(struct cln *cln, SVCXPRT *transp, char *netid,
1073 1073 struct netbuf *nbuf)
1074 1074 {
1075 1075 if ((cln->transp = transp) != NULL) {
1076 1076 assert(netid == NULL && nbuf == NULL);
1077 1077 cln->netid = transp->xp_netid;
1078 1078 cln->nbuf = svc_getrpccaller(transp);
1079 1079 } else {
1080 1080 cln->netid = netid;
1081 1081 cln->nbuf = nbuf;
1082 1082 }
1083 1083
1084 1084 cln->nconf = NULL;
1085 1085 cln->clnames = NULL;
1086 1086 cln->host = NULL;
1087 1087
1088 1088 cln->flags = 0;
1089 1089 }
1090 1090
1091 1091 void
1092 1092 cln_init(struct cln *cln, SVCXPRT *transp)
1093 1093 {
1094 1094 cln_init_common(cln, transp, NULL, NULL);
1095 1095 }
1096 1096
1097 1097 void
1098 1098 cln_init_lazy(struct cln *cln, char *netid, struct netbuf *nbuf)
1099 1099 {
1100 1100 cln_init_common(cln, NULL, netid, nbuf);
1101 1101 }
1102 1102
1103 1103 void
1104 1104 cln_fini(struct cln *cln)
1105 1105 {
1106 1106 if (cln->nconf != NULL)
1107 1107 freenetconfigent(cln->nconf);
1108 1108
1109 1109 if (cln->clnames != NULL)
1110 1110 netdir_free(cln->clnames, ND_HOSTSERVLIST);
1111 1111
1112 1112 free(cln->host);
1113 1113 }
1114 1114
1115 1115 struct netbuf *
1116 1116 cln_getnbuf(struct cln *cln)
1117 1117 {
1118 1118 return (cln->nbuf);
1119 1119 }
1120 1120
1121 1121 struct nd_hostservlist *
1122 1122 cln_getclientsnames(struct cln *cln)
1123 1123 {
1124 1124 if ((cln->flags & CLN_CLNAMES) == 0) {
1125 1125 /*
1126 1126 * nconf is not needed if we do not have nbuf (see
1127 1127 * cln_gethost() too), so we check for nbuf and in a case it is
1128 1128 * NULL we do not try to get nconf.
1129 1129 */
1130 1130 if (cln->netid != NULL && cln->nbuf != NULL) {
1131 1131 cln->nconf = getnetconfigent(cln->netid);
1132 1132 if (cln->nconf == NULL)
1133 1133 syslog(LOG_ERR, "%s: getnetconfigent failed",
1134 1134 cln->netid);
1135 1135 }
1136 1136
1137 1137 if (cln->nconf != NULL && cln->nbuf != NULL)
1138 1138 (void) __netdir_getbyaddr_nosrv(cln->nconf,
1139 1139 &cln->clnames, cln->nbuf);
1140 1140
1141 1141 cln->flags |= CLN_CLNAMES;
1142 1142 }
1143 1143
1144 1144 return (cln->clnames);
1145 1145 }
1146 1146
1147 1147 /*
1148 1148 * Return B_TRUE if the host is already available at no cost
1149 1149 */
1150 1150 boolean_t
1151 1151 cln_havehost(struct cln *cln)
1152 1152 {
1153 1153 return ((cln->flags & (CLN_CLNAMES | CLN_HOST)) != 0);
1154 1154 }
1155 1155
1156 1156 char *
1157 1157 cln_gethost(struct cln *cln)
1158 1158 {
1159 1159 if (cln_getclientsnames(cln) != NULL)
1160 1160 return (cln->clnames->h_hostservs[0].h_host);
1161 1161
1162 1162 if ((cln->flags & CLN_HOST) == 0) {
1163 1163 if (cln->nconf == NULL || cln->nbuf == NULL) {
1164 1164 cln->host = strdup("(anon)");
1165 1165 } else {
1166 1166 char host[MAXIPADDRLEN];
1167 1167
1168 1168 if (strcmp(cln->nconf->nc_protofmly, NC_INET) == 0) {
1169 1169 struct sockaddr_in *sa;
1170 1170
1171 1171 /* LINTED pointer alignment */
1172 1172 sa = (struct sockaddr_in *)(cln->nbuf->buf);
1173 1173 (void) inet_ntoa_r(sa->sin_addr, host);
1174 1174
1175 1175 cln->host = strdup(host);
1176 1176 } else if (strcmp(cln->nconf->nc_protofmly,
1177 1177 NC_INET6) == 0) {
1178 1178 struct sockaddr_in6 *sa;
1179 1179
1180 1180 /* LINTED pointer alignment */
1181 1181 sa = (struct sockaddr_in6 *)(cln->nbuf->buf);
1182 1182 (void) inet_ntop(AF_INET6,
1183 1183 sa->sin6_addr.s6_addr,
1184 1184 host, INET6_ADDRSTRLEN);
1185 1185
1186 1186 cln->host = strdup(host);
1187 1187 } else {
1188 1188 syslog(LOG_ERR, gettext("Client's address is "
1189 1189 "neither IPv4 nor IPv6"));
1190 1190
1191 1191 cln->host = strdup("(anon)");
1192 1192 }
1193 1193 }
1194 1194
1195 1195 cln->flags |= CLN_HOST;
1196 1196 }
1197 1197
1198 1198 return (cln->host);
1199 1199 }
1200 1200
1201 1201 /*
1202 1202 * Check mount requests, add to mounted list if ok
1203 1203 */
1204 1204 static int
1205 1205 mount(struct svc_req *rqstp)
1206 1206 {
1207 1207 SVCXPRT *transp;
1208 1208 int version, vers;
1209 1209 struct fhstatus fhs;
1210 1210 struct mountres3 mountres3;
1211 1211 char fh[FHSIZE3];
1212 1212 int len = FHSIZE3;
1213 1213 char *path, rpath[MAXPATHLEN];
1214 1214 share_t *sh = NULL;
1215 1215 struct cln cln;
1216 1216 char *host = NULL;
1217 1217 int error = 0, lofs_tried = 0, enqueued;
1218 1218 int flavor_list[MAX_FLAVORS];
1219 1219 int flavor_count;
1220 1220 ucred_t *uc = NULL;
1221 1221
1222 1222 int audit_status;
1223 1223
1224 1224 transp = rqstp->rq_xprt;
1225 1225 version = rqstp->rq_vers;
1226 1226 path = NULL;
1227 1227
1228 1228 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
1229 1229 svcerr_decode(transp);
1230 1230 return (EACCES);
1231 1231 }
1232 1232
1233 1233 cln_init(&cln, transp);
1234 1234
1235 1235 /*
1236 1236 * Put off getting the name for the client until we
1237 1237 * need it. This is a performance gain. If we are logging,
1238 1238 * then we don't care about performance and might as well
1239 1239 * get the host name now in case we need to spit out an
1240 1240 * error message.
1241 1241 */
1242 1242 if (verbose) {
1243 1243 DTRACE_PROBE(mountd, name_by_verbose);
1244 1244 if ((host = cln_gethost(&cln)) == NULL) {
1245 1245 /*
1246 1246 * We failed to get a name for the client, even
1247 1247 * 'anon', probably because we ran out of memory.
1248 1248 * In this situation it doesn't make sense to
1249 1249 * allow the mount to succeed.
1250 1250 */
1251 1251 error = EACCES;
1252 1252 goto reply;
1253 1253 }
1254 1254 }
1255 1255
1256 1256 /*
1257 1257 * If the version being used is less than the minimum version,
1258 1258 * the filehandle translation should not be provided to the
1259 1259 * client.
1260 1260 */
1261 1261 if (rejecting || version < mount_vers_min) {
1262 1262 if (verbose)
1263 1263 syslog(LOG_NOTICE, "Rejected mount: %s for %s",
1264 1264 host, path);
1265 1265 error = EACCES;
1266 1266 goto reply;
1267 1267 }
1268 1268
1269 1269 /*
1270 1270 * Trusted Extension doesn't support nfsv2. nfsv2 client
1271 1271 * uses MOUNT protocol v1 and v2. To prevent circumventing
1272 1272 * TX label policy via using nfsv2 client, reject a mount
1273 1273 * request with version less than 3 and log an error.
1274 1274 */
1275 1275 if (is_system_labeled()) {
1276 1276 if (version < 3) {
1277 1277 if (verbose)
1278 1278 syslog(LOG_ERR,
1279 1279 "Rejected mount: TX doesn't support NFSv2");
1280 1280 error = EACCES;
1281 1281 goto reply;
1282 1282 }
1283 1283 }
1284 1284
1285 1285 /*
1286 1286 * Get the real path (no symbolic links in it)
1287 1287 */
1288 1288 if (realpath(path, rpath) == NULL) {
1289 1289 error = errno;
1290 1290 if (verbose)
1291 1291 syslog(LOG_ERR,
1292 1292 "mount request: realpath: %s: %m", path);
1293 1293 if (error == ENOENT)
1294 1294 error = mount_enoent_error(&cln, path, rpath,
1295 1295 flavor_list);
1296 1296 goto reply;
1297 1297 }
1298 1298
1299 1299 if ((sh = findentry(rpath)) == NULL &&
1300 1300 (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
1301 1301 error = EACCES;
1302 1302 goto reply;
1303 1303 }
1304 1304
1305 1305 /*
1306 1306 * Check if this is a "nosub" only export, in which case, mounting
1307 1307 * subdirectories isn't allowed. Bug 1184573.
1308 1308 */
1309 1309 if (checkrootmount(sh, rpath) == 0) {
1310 1310 error = EACCES;
1311 1311 goto reply;
1312 1312 }
1313 1313
1314 1314 if (newopts(sh->sh_opts))
1315 1315 flavor_count = getclientsflavors_new(sh, &cln, flavor_list);
1316 1316 else
1317 1317 flavor_count = getclientsflavors_old(sh, &cln, flavor_list);
1318 1318
1319 1319 if (flavor_count == 0) {
1320 1320 error = EACCES;
1321 1321 goto reply;
1322 1322 }
1323 1323
1324 1324 /*
1325 1325 * Check MAC policy here. The server side policy should be
1326 1326 * consistent with client side mount policy, i.e.
1327 1327 * - we disallow an admin_low unlabeled client to mount
1328 1328 * - we disallow mount from a lower labeled client.
1329 1329 */
1330 1330 if (is_system_labeled()) {
1331 1331 m_label_t *clabel = NULL;
1332 1332 m_label_t *slabel = NULL;
1333 1333 m_label_t admin_low;
1334 1334
1335 1335 if (svc_getcallerucred(rqstp->rq_xprt, &uc) != 0) {
1336 1336 syslog(LOG_ERR,
1337 1337 "mount request: Failed to get caller's ucred : %m");
1338 1338 error = EACCES;
1339 1339 goto reply;
1340 1340 }
1341 1341 if ((clabel = ucred_getlabel(uc)) == NULL) {
1342 1342 syslog(LOG_ERR,
1343 1343 "mount request: can't get client label from ucred");
1344 1344 error = EACCES;
1345 1345 goto reply;
1346 1346 }
1347 1347
1348 1348 bsllow(&admin_low);
1349 1349 if (blequal(&admin_low, clabel)) {
1350 1350 struct sockaddr *ca;
1351 1351 tsol_tpent_t *tp;
1352 1352
1353 1353 ca = (struct sockaddr *)(void *)svc_getrpccaller(
1354 1354 rqstp->rq_xprt)->buf;
1355 1355 if (ca == NULL) {
1356 1356 error = EACCES;
1357 1357 goto reply;
1358 1358 }
1359 1359 /*
1360 1360 * get trusted network template associated
1361 1361 * with the client.
1362 1362 */
1363 1363 tp = get_client_template(ca);
1364 1364 if (tp == NULL || tp->host_type != SUN_CIPSO) {
1365 1365 if (tp != NULL)
1366 1366 tsol_freetpent(tp);
1367 1367 error = EACCES;
1368 1368 goto reply;
1369 1369 }
1370 1370 tsol_freetpent(tp);
1371 1371 } else {
1372 1372 if ((slabel = m_label_alloc(MAC_LABEL)) == NULL) {
1373 1373 error = EACCES;
1374 1374 goto reply;
1375 1375 }
1376 1376
1377 1377 if (getlabel(rpath, slabel) != 0) {
1378 1378 m_label_free(slabel);
1379 1379 error = EACCES;
1380 1380 goto reply;
1381 1381 }
1382 1382
1383 1383 if (!bldominates(clabel, slabel)) {
1384 1384 m_label_free(slabel);
1385 1385 error = EACCES;
1386 1386 goto reply;
1387 1387 }
1388 1388 m_label_free(slabel);
1389 1389 }
1390 1390 }
1391 1391
1392 1392 /*
1393 1393 * Now get the filehandle.
1394 1394 *
1395 1395 * NFS V2 clients get a 32 byte filehandle.
1396 1396 * NFS V3 clients get a 32 or 64 byte filehandle, depending on
1397 1397 * the embedded FIDs.
1398 1398 */
1399 1399 vers = (version == MOUNTVERS3) ? NFS_V3 : NFS_VERSION;
1400 1400
1401 1401 /* LINTED pointer alignment */
1402 1402 while (nfs_getfh(rpath, vers, &len, fh) < 0) {
1403 1403 if (errno == EINVAL &&
1404 1404 (sh = find_lofsentry(rpath, &lofs_tried)) != NULL) {
1405 1405 errno = 0;
1406 1406 continue;
1407 1407 }
1408 1408 error = errno == EINVAL ? EACCES : errno;
1409 1409 syslog(LOG_DEBUG, "mount request: getfh failed on %s: %m",
1410 1410 path);
1411 1411 break;
1412 1412 }
1413 1413
1414 1414 if (version == MOUNTVERS3) {
1415 1415 mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len = len;
1416 1416 mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val = fh;
1417 1417 } else {
1418 1418 bcopy(fh, &fhs.fhstatus_u.fhs_fhandle, NFS_FHSIZE);
1419 1419 }
1420 1420
1421 1421 reply:
1422 1422 if (uc != NULL)
1423 1423 ucred_free(uc);
1424 1424
1425 1425 switch (version) {
1426 1426 case MOUNTVERS:
1427 1427 case MOUNTVERS_POSIX:
1428 1428 if (error == EINVAL)
1429 1429 fhs.fhs_status = NFSERR_ACCES;
1430 1430 else if (error == EREMOTE)
1431 1431 fhs.fhs_status = NFSERR_REMOTE;
1432 1432 else
1433 1433 fhs.fhs_status = error;
1434 1434
1435 1435 if (!svc_sendreply(transp, xdr_fhstatus, (char *)&fhs))
1436 1436 log_cant_reply_cln(&cln);
1437 1437
1438 1438 audit_status = fhs.fhs_status;
1439 1439 break;
1440 1440
1441 1441 case MOUNTVERS3:
1442 1442 if (!error) {
1443 1443 mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val =
1444 1444 flavor_list;
1445 1445 mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_len =
1446 1446 flavor_count;
1447 1447
1448 1448 } else if (error == ENAMETOOLONG)
1449 1449 error = MNT3ERR_NAMETOOLONG;
1450 1450
1451 1451 mountres3.fhs_status = error;
1452 1452 if (!svc_sendreply(transp, xdr_mountres3, (char *)&mountres3))
1453 1453 log_cant_reply_cln(&cln);
1454 1454
1455 1455 audit_status = mountres3.fhs_status;
1456 1456 break;
1457 1457 }
1458 1458
1459 1459 if (cln_havehost(&cln))
1460 1460 host = cln_gethost(&cln);
1461 1461
1462 1462 if (verbose)
1463 1463 syslog(LOG_NOTICE, "MOUNT: %s %s %s",
1464 1464 (host == NULL) ? "unknown host" : host,
1465 1465 error ? "denied" : "mounted", path);
1466 1466
1467 1467 /*
1468 1468 * If we can not create a queue entry, go ahead and do it
1469 1469 * in the context of this thread.
1470 1470 */
1471 1471 enqueued = enqueue_logging_data(host, transp, path, rpath,
1472 1472 audit_status, error);
1473 1473 if (enqueued == FALSE) {
1474 1474 if (host == NULL) {
1475 1475 DTRACE_PROBE(mountd, name_by_in_thread);
1476 1476 host = cln_gethost(&cln);
1477 1477 }
1478 1478
1479 1479 DTRACE_PROBE(mountd, logged_in_thread);
1480 1480 audit_mountd_mount(host, path, audit_status); /* BSM */
1481 1481 if (!error)
1482 1482 mntlist_new(host, rpath); /* add entry to mount list */
1483 1483 }
1484 1484
1485 1485 if (path != NULL)
1486 1486 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
1487 1487
1488 1488 if (sh)
1489 1489 sharefree(sh);
1490 1490
1491 1491 cln_fini(&cln);
1492 1492
1493 1493 return (error);
1494 1494 }
1495 1495
1496 1496 /*
1497 1497 * Determine whether two paths are within the same file system.
1498 1498 * Returns nonzero (true) if paths are the same, zero (false) if
1499 1499 * they are different. If an error occurs, return false.
1500 1500 *
1501 1501 * Use the actual FSID if it's available (via getattrat()); otherwise,
1502 1502 * fall back on st_dev.
1503 1503 *
1504 1504 * With ZFS snapshots, st_dev differs from the regular file system
1505 1505 * versus the snapshot. But the fsid is the same throughout. Thus
1506 1506 * the fsid is a better test.
1507 1507 */
1508 1508 static int
1509 1509 same_file_system(const char *path1, const char *path2)
1510 1510 {
1511 1511 uint64_t fsid1, fsid2;
1512 1512 struct stat64 st1, st2;
1513 1513 nvlist_t *nvl1 = NULL;
1514 1514 nvlist_t *nvl2 = NULL;
1515 1515
1516 1516 if ((getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path1, &nvl1) == 0) &&
1517 1517 (getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path2, &nvl2) == 0) &&
1518 1518 (nvlist_lookup_uint64(nvl1, A_FSID, &fsid1) == 0) &&
1519 1519 (nvlist_lookup_uint64(nvl2, A_FSID, &fsid2) == 0)) {
1520 1520 nvlist_free(nvl1);
1521 1521 nvlist_free(nvl2);
↓ open down ↓ |
1521 lines elided |
↑ open up ↑ |
1522 1522 /*
1523 1523 * We have found fsid's for both paths.
1524 1524 */
1525 1525
1526 1526 if (fsid1 == fsid2)
1527 1527 return (B_TRUE);
1528 1528
1529 1529 return (B_FALSE);
1530 1530 }
1531 1531
1532 - if (nvl1 != NULL)
1533 - nvlist_free(nvl1);
1534 - if (nvl2 != NULL)
1535 - nvlist_free(nvl2);
1532 + nvlist_free(nvl1);
1533 + nvlist_free(nvl2);
1536 1534
1537 1535 /*
1538 1536 * We were unable to find fsid's for at least one of the paths.
1539 1537 * fall back on st_dev.
1540 1538 */
1541 1539
1542 1540 if (stat64(path1, &st1) < 0) {
1543 1541 syslog(LOG_NOTICE, "%s: %m", path1);
1544 1542 return (B_FALSE);
1545 1543 }
1546 1544 if (stat64(path2, &st2) < 0) {
1547 1545 syslog(LOG_NOTICE, "%s: %m", path2);
1548 1546 return (B_FALSE);
1549 1547 }
1550 1548
1551 1549 if (st1.st_dev == st2.st_dev)
1552 1550 return (B_TRUE);
1553 1551
1554 1552 return (B_FALSE);
1555 1553 }
1556 1554
1557 1555 share_t *
1558 1556 findentry(char *path)
1559 1557 {
1560 1558 share_t *sh = NULL;
1561 1559 struct sh_list *shp;
1562 1560 char *p1, *p2;
1563 1561
1564 1562 check_sharetab();
1565 1563
1566 1564 (void) rw_rdlock(&sharetab_lock);
1567 1565
1568 1566 for (shp = share_list; shp; shp = shp->shl_next) {
1569 1567 sh = shp->shl_sh;
1570 1568 for (p1 = sh->sh_path, p2 = path; *p1 == *p2; p1++, p2++)
1571 1569 if (*p1 == '\0')
1572 1570 goto done; /* exact match */
1573 1571
1574 1572 /*
1575 1573 * Now compare the pathnames for three cases:
1576 1574 *
1577 1575 * Parent: /export/foo (no trailing slash on parent)
1578 1576 * Child: /export/foo/bar
1579 1577 *
1580 1578 * Parent: /export/foo/ (trailing slash on parent)
1581 1579 * Child: /export/foo/bar
1582 1580 *
1583 1581 * Parent: /export/foo/ (no trailing slash on child)
1584 1582 * Child: /export/foo
1585 1583 */
1586 1584 if ((*p1 == '\0' && *p2 == '/') ||
1587 1585 (*p1 == '\0' && *(p1-1) == '/') ||
1588 1586 (*p2 == '\0' && *p1 == '/' && *(p1+1) == '\0')) {
1589 1587 /*
1590 1588 * We have a subdirectory. Test whether the
1591 1589 * subdirectory is in the same file system.
1592 1590 */
1593 1591 if (same_file_system(path, sh->sh_path))
1594 1592 goto done;
1595 1593 }
1596 1594 }
1597 1595 done:
1598 1596 sh = shp ? sharedup(sh) : NULL;
1599 1597
1600 1598 (void) rw_unlock(&sharetab_lock);
1601 1599
1602 1600 return (sh);
1603 1601 }
1604 1602
1605 1603
1606 1604 static int
1607 1605 is_substring(char **mntp, char **path)
1608 1606 {
1609 1607 char *p1 = *mntp, *p2 = *path;
1610 1608
1611 1609 if (*p1 == '\0' && *p2 == '\0') /* exact match */
1612 1610 return (1);
1613 1611 else if (*p1 == '\0' && *p2 == '/')
1614 1612 return (1);
1615 1613 else if (*p1 == '\0' && *(p1-1) == '/') {
1616 1614 *path = --p2; /* we need the slash in p2 */
1617 1615 return (1);
1618 1616 } else if (*p2 == '\0') {
1619 1617 while (*p1 == '/')
1620 1618 p1++;
1621 1619 if (*p1 == '\0') /* exact match */
1622 1620 return (1);
1623 1621 }
1624 1622 return (0);
1625 1623 }
1626 1624
1627 1625 /*
1628 1626 * find_lofsentry() searches for the real path which this requested LOFS path
1629 1627 * (rpath) shadows. If found, it will return the sharetab entry of
1630 1628 * the real path that corresponds to the LOFS path.
1631 1629 * We first search mnttab to see if the requested path is an automounted
1632 1630 * path. If it is an automounted path, it will trigger the mount by stat()ing
1633 1631 * the requested path. Note that it is important to check that this path is
1634 1632 * actually an automounted path, otherwise we would stat() a path which may
1635 1633 * turn out to be NFS and block indefinitely on a dead server. The automounter
1636 1634 * times-out if the server is dead, so there's no risk of hanging this
1637 1635 * thread waiting for stat().
1638 1636 * After the mount has been triggered (if necessary), we look for a
1639 1637 * mountpoint of type LOFS (by searching /etc/mnttab again) which
1640 1638 * is a substring of the rpath. If found, we construct a new path by
1641 1639 * concatenating the mnt_special and the remaining of rpath, call findentry()
1642 1640 * to make sure the 'real path' is shared.
1643 1641 */
1644 1642 static share_t *
1645 1643 find_lofsentry(char *rpath, int *done_flag)
1646 1644 {
1647 1645 struct stat r_stbuf;
1648 1646 mntlist_t *ml, *mntl, *mntpnt = NULL;
1649 1647 share_t *retcode = NULL;
1650 1648 char tmp_path[MAXPATHLEN];
1651 1649 int mntpnt_len = 0, tmp;
1652 1650 char *p1, *p2;
1653 1651
1654 1652 if ((*done_flag)++)
1655 1653 return (retcode);
1656 1654
1657 1655 /*
1658 1656 * While fsgetmntlist() uses lockf() to
1659 1657 * lock the mnttab before reading it in,
1660 1658 * the lock ignores threads in the same process.
1661 1659 * Read in the mnttab with the protection of a mutex.
1662 1660 */
1663 1661 (void) mutex_lock(&mnttab_lock);
1664 1662 mntl = fsgetmntlist();
1665 1663 (void) mutex_unlock(&mnttab_lock);
1666 1664
1667 1665 /*
1668 1666 * Obtain the mountpoint for the requested path.
1669 1667 */
1670 1668 for (ml = mntl; ml; ml = ml->mntl_next) {
1671 1669 for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1672 1670 *p1 == *p2 && *p1; p1++, p2++)
1673 1671 ;
1674 1672 if (is_substring(&p1, &p2) &&
1675 1673 (tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len) {
1676 1674 mntpnt = ml;
1677 1675 mntpnt_len = tmp;
1678 1676 }
1679 1677 }
1680 1678
1681 1679 /*
1682 1680 * If the path needs to be autoFS mounted, trigger the mount by
1683 1681 * stat()ing it. This is determined by checking whether the
1684 1682 * mountpoint we just found is of type autofs.
1685 1683 */
1686 1684 if (mntpnt != NULL &&
1687 1685 strcmp(mntpnt->mntl_mnt->mnt_fstype, "autofs") == 0) {
1688 1686 /*
1689 1687 * The requested path is a substring of an autoFS filesystem.
1690 1688 * Trigger the mount.
1691 1689 */
1692 1690 if (stat(rpath, &r_stbuf) < 0) {
1693 1691 if (verbose)
1694 1692 syslog(LOG_NOTICE, "%s: %m", rpath);
1695 1693 goto done;
1696 1694 }
1697 1695 if ((r_stbuf.st_mode & S_IFMT) == S_IFDIR) {
1698 1696 /*
1699 1697 * The requested path is a directory, stat(2) it
1700 1698 * again with a trailing '.' to force the autoFS
1701 1699 * module to trigger the mount of indirect
1702 1700 * automount entries, such as /net/jurassic/.
1703 1701 */
1704 1702 if (strlen(rpath) + 2 > MAXPATHLEN) {
1705 1703 if (verbose) {
1706 1704 syslog(LOG_NOTICE,
1707 1705 "%s/.: exceeds MAXPATHLEN %d",
1708 1706 rpath, MAXPATHLEN);
1709 1707 }
1710 1708 goto done;
1711 1709 }
1712 1710 (void) strcpy(tmp_path, rpath);
1713 1711 (void) strcat(tmp_path, "/.");
1714 1712
1715 1713 if (stat(tmp_path, &r_stbuf) < 0) {
1716 1714 if (verbose)
1717 1715 syslog(LOG_NOTICE, "%s: %m", tmp_path);
1718 1716 goto done;
1719 1717 }
1720 1718 }
1721 1719
1722 1720 /*
1723 1721 * The mount has been triggered, re-read mnttab to pick up
1724 1722 * the changes made by autoFS.
1725 1723 */
1726 1724 fsfreemntlist(mntl);
1727 1725 (void) mutex_lock(&mnttab_lock);
1728 1726 mntl = fsgetmntlist();
1729 1727 (void) mutex_unlock(&mnttab_lock);
1730 1728 }
1731 1729
1732 1730 /*
1733 1731 * The autoFS mountpoint has been triggered if necessary,
1734 1732 * now search mnttab again to determine if the requested path
1735 1733 * is an LOFS mount of a shared path.
1736 1734 */
1737 1735 mntpnt_len = 0;
1738 1736 for (ml = mntl; ml; ml = ml->mntl_next) {
1739 1737 if (strcmp(ml->mntl_mnt->mnt_fstype, "lofs"))
1740 1738 continue;
1741 1739
1742 1740 for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1743 1741 *p1 == *p2 && *p1; p1++, p2++)
1744 1742 ;
1745 1743
1746 1744 if (is_substring(&p1, &p2) &&
1747 1745 ((tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len)) {
1748 1746 mntpnt_len = tmp;
1749 1747
1750 1748 if ((strlen(ml->mntl_mnt->mnt_special) + strlen(p2)) >
1751 1749 MAXPATHLEN) {
1752 1750 if (verbose) {
1753 1751 syslog(LOG_NOTICE, "%s%s: exceeds %d",
1754 1752 ml->mntl_mnt->mnt_special, p2,
1755 1753 MAXPATHLEN);
1756 1754 }
1757 1755 if (retcode)
1758 1756 sharefree(retcode);
1759 1757 retcode = NULL;
1760 1758 goto done;
1761 1759 }
1762 1760
1763 1761 (void) strcpy(tmp_path, ml->mntl_mnt->mnt_special);
1764 1762 (void) strcat(tmp_path, p2);
1765 1763 if (retcode)
1766 1764 sharefree(retcode);
1767 1765 retcode = findentry(tmp_path);
1768 1766 }
1769 1767 }
1770 1768
1771 1769 if (retcode) {
1772 1770 assert(strlen(tmp_path) > 0);
1773 1771 (void) strcpy(rpath, tmp_path);
1774 1772 }
1775 1773
1776 1774 done:
1777 1775 fsfreemntlist(mntl);
1778 1776 return (retcode);
1779 1777 }
1780 1778
1781 1779 /*
1782 1780 * Determine whether an access list grants rights to a particular host.
1783 1781 * We match on aliases of the hostname as well as on the canonical name.
1784 1782 * Names in the access list may be either hosts or netgroups; they're
1785 1783 * not distinguished syntactically. We check for hosts first because
1786 1784 * it's cheaper, then try netgroups.
1787 1785 *
1788 1786 * Return values:
1789 1787 * 1 - access is granted
1790 1788 * 0 - access is denied
1791 1789 * -1 - an error occured
1792 1790 */
1793 1791 int
1794 1792 in_access_list(struct cln *cln,
1795 1793 char *access_list) /* N.B. we clobber this "input" parameter */
1796 1794 {
1797 1795 char addr[INET_ADDRSTRLEN];
1798 1796 char buff[256];
1799 1797 int nentries = 0;
1800 1798 char *cstr = access_list;
1801 1799 char *gr = access_list;
1802 1800 int i;
1803 1801 int response;
1804 1802 struct netbuf *pnb;
1805 1803 struct nd_hostservlist *clnames = NULL;
1806 1804
1807 1805 /* If no access list - then it's unrestricted */
1808 1806 if (access_list == NULL || *access_list == '\0')
1809 1807 return (1);
1810 1808
1811 1809 if ((pnb = cln_getnbuf(cln)) == NULL)
1812 1810 return (-1);
1813 1811
1814 1812 for (;;) {
1815 1813 if ((cstr = strpbrk(cstr, "[:")) != NULL) {
1816 1814 if (*cstr == ':') {
1817 1815 *cstr = '\0';
1818 1816 } else {
1819 1817 assert(*cstr == '[');
1820 1818 cstr = strchr(cstr + 1, ']');
1821 1819 if (cstr == NULL)
1822 1820 return (-1);
1823 1821 cstr++;
1824 1822 continue;
1825 1823 }
1826 1824 }
1827 1825
1828 1826 /*
1829 1827 * If the list name has a '-' prepended then a match of
1830 1828 * the following name implies failure instead of success.
1831 1829 */
1832 1830 if (*gr == '-') {
1833 1831 response = 0;
1834 1832 gr++;
1835 1833 } else {
1836 1834 response = 1;
1837 1835 }
1838 1836
1839 1837 /*
1840 1838 * First check if we have '@' entry, as it doesn't
1841 1839 * require client hostname.
1842 1840 */
1843 1841 if (*gr == '@') {
1844 1842 gr++;
1845 1843
1846 1844 /* Netname support */
1847 1845 if (!isdigit(*gr) && *gr != '[') {
1848 1846 struct netent n, *np;
1849 1847
1850 1848 if ((np = getnetbyname_r(gr, &n, buff,
1851 1849 sizeof (buff))) != NULL &&
1852 1850 np->n_net != 0) {
1853 1851 while ((np->n_net & 0xFF000000u) == 0)
1854 1852 np->n_net <<= 8;
1855 1853 np->n_net = htonl(np->n_net);
1856 1854 if (inet_ntop(AF_INET, &np->n_net, addr,
1857 1855 INET_ADDRSTRLEN) == NULL)
1858 1856 break;
1859 1857 if (inet_matchaddr(pnb->buf, addr))
1860 1858 return (response);
1861 1859 }
1862 1860 } else {
1863 1861 if (inet_matchaddr(pnb->buf, gr))
1864 1862 return (response);
1865 1863 }
1866 1864
1867 1865 goto next;
1868 1866 }
1869 1867
1870 1868 /*
1871 1869 * No other checks can be performed if client address
1872 1870 * can't be resolved.
1873 1871 */
1874 1872 if ((clnames = cln_getclientsnames(cln)) == NULL)
1875 1873 goto next;
1876 1874
1877 1875 /* Otherwise loop through all client hostname aliases */
1878 1876 for (i = 0; i < clnames->h_cnt; i++) {
1879 1877 char *host = clnames->h_hostservs[i].h_host;
1880 1878
1881 1879 /*
1882 1880 * If the list name begins with a dot then
1883 1881 * do a domain name suffix comparison.
1884 1882 * A single dot matches any name with no
1885 1883 * suffix.
1886 1884 */
1887 1885 if (*gr == '.') {
1888 1886 if (*(gr + 1) == '\0') { /* single dot */
1889 1887 if (strchr(host, '.') == NULL)
1890 1888 return (response);
1891 1889 } else {
1892 1890 int off = strlen(host) - strlen(gr);
1893 1891 if (off > 0 &&
1894 1892 strcasecmp(host + off, gr) == 0) {
1895 1893 return (response);
1896 1894 }
1897 1895 }
1898 1896 } else {
1899 1897 /* Just do a hostname match */
1900 1898 if (strcasecmp(gr, host) == 0)
1901 1899 return (response);
1902 1900 }
1903 1901 }
1904 1902
1905 1903 nentries++;
1906 1904
1907 1905 next:
1908 1906 if (cstr == NULL)
1909 1907 break;
1910 1908
1911 1909 gr = ++cstr;
1912 1910 }
1913 1911
1914 1912 if (clnames == NULL)
1915 1913 return (0);
1916 1914
1917 1915 return (netgroup_check(clnames, access_list, nentries));
1918 1916 }
1919 1917
1920 1918
1921 1919 static char *optlist[] = {
1922 1920 #define OPT_RO 0
1923 1921 SHOPT_RO,
1924 1922 #define OPT_RW 1
1925 1923 SHOPT_RW,
1926 1924 #define OPT_ROOT 2
1927 1925 SHOPT_ROOT,
1928 1926 #define OPT_SECURE 3
1929 1927 SHOPT_SECURE,
1930 1928 #define OPT_ANON 4
1931 1929 SHOPT_ANON,
1932 1930 #define OPT_WINDOW 5
1933 1931 SHOPT_WINDOW,
1934 1932 #define OPT_NOSUID 6
1935 1933 SHOPT_NOSUID,
1936 1934 #define OPT_ACLOK 7
1937 1935 SHOPT_ACLOK,
1938 1936 #define OPT_SEC 8
1939 1937 SHOPT_SEC,
1940 1938 #define OPT_NONE 9
1941 1939 SHOPT_NONE,
1942 1940 #define OPT_UIDMAP 10
1943 1941 SHOPT_UIDMAP,
1944 1942 #define OPT_GIDMAP 11
1945 1943 SHOPT_GIDMAP,
1946 1944 NULL
1947 1945 };
1948 1946
1949 1947 static int
1950 1948 map_flavor(char *str)
1951 1949 {
1952 1950 seconfig_t sec;
1953 1951
1954 1952 if (nfs_getseconfig_byname(str, &sec))
1955 1953 return (-1);
1956 1954
1957 1955 return (sec.sc_nfsnum);
1958 1956 }
1959 1957
1960 1958 /*
1961 1959 * If the option string contains a "sec="
1962 1960 * option, then use new option syntax.
1963 1961 */
1964 1962 static int
1965 1963 newopts(char *opts)
1966 1964 {
1967 1965 char *head, *p, *val;
1968 1966
1969 1967 if (!opts || *opts == '\0')
1970 1968 return (0);
1971 1969
1972 1970 head = strdup(opts);
1973 1971 if (head == NULL) {
1974 1972 syslog(LOG_ERR, "opts: no memory");
1975 1973 return (0);
1976 1974 }
1977 1975
1978 1976 p = head;
1979 1977 while (*p) {
1980 1978 if (getsubopt(&p, optlist, &val) == OPT_SEC) {
1981 1979 free(head);
1982 1980 return (1);
1983 1981 }
1984 1982 }
1985 1983
1986 1984 free(head);
1987 1985 return (0);
1988 1986 }
1989 1987
1990 1988 /*
1991 1989 * Given an export and the clients hostname(s)
1992 1990 * determine the security flavors that this
1993 1991 * client is permitted to use.
1994 1992 *
1995 1993 * This routine is called only for "old" syntax, i.e.
1996 1994 * only one security flavor is allowed. So we need
1997 1995 * to determine two things: the particular flavor,
1998 1996 * and whether the client is allowed to use this
1999 1997 * flavor, i.e. is in the access list.
2000 1998 *
2001 1999 * Note that if there is no access list, then the
2002 2000 * default is that access is granted.
2003 2001 */
2004 2002 static int
2005 2003 getclientsflavors_old(share_t *sh, struct cln *cln, int *flavors)
2006 2004 {
2007 2005 char *opts, *p, *val;
2008 2006 boolean_t ok = B_FALSE;
2009 2007 int defaultaccess = 1;
2010 2008 boolean_t reject = B_FALSE;
2011 2009
2012 2010 opts = strdup(sh->sh_opts);
2013 2011 if (opts == NULL) {
2014 2012 syslog(LOG_ERR, "getclientsflavors: no memory");
2015 2013 return (0);
2016 2014 }
2017 2015
2018 2016 flavors[0] = AUTH_SYS;
2019 2017 p = opts;
2020 2018
2021 2019 while (*p) {
2022 2020
2023 2021 switch (getsubopt(&p, optlist, &val)) {
2024 2022 case OPT_SECURE:
2025 2023 flavors[0] = AUTH_DES;
2026 2024 break;
2027 2025
2028 2026 case OPT_RO:
2029 2027 case OPT_RW:
2030 2028 defaultaccess = 0;
2031 2029 if (in_access_list(cln, val) > 0)
2032 2030 ok = B_TRUE;
2033 2031 break;
2034 2032
2035 2033 case OPT_NONE:
2036 2034 defaultaccess = 0;
2037 2035 if (in_access_list(cln, val) > 0)
2038 2036 reject = B_TRUE;
2039 2037 }
2040 2038 }
2041 2039
2042 2040 free(opts);
2043 2041
2044 2042 /* none takes precedence over everything else */
2045 2043 if (reject)
2046 2044 ok = B_FALSE;
2047 2045
2048 2046 return (defaultaccess || ok);
2049 2047 }
2050 2048
2051 2049 /*
2052 2050 * Given an export and the clients hostname(s)
2053 2051 * determine the security flavors that this
2054 2052 * client is permitted to use.
2055 2053 *
2056 2054 * This is somewhat more complicated than the "old"
2057 2055 * routine because the options may contain multiple
2058 2056 * security flavors (sec=) each with its own access
2059 2057 * lists. So a client could be granted access based
2060 2058 * on a number of security flavors. Note that the
2061 2059 * type of access might not always be the same, the
2062 2060 * client may get readonly access with one flavor
2063 2061 * and readwrite with another, however the client
2064 2062 * is not told this detail, it gets only the list
2065 2063 * of flavors, and only if the client is using
2066 2064 * version 3 of the mount protocol.
2067 2065 */
2068 2066 static int
2069 2067 getclientsflavors_new(share_t *sh, struct cln *cln, int *flavors)
2070 2068 {
2071 2069 char *opts, *p, *val;
2072 2070 char *lasts;
2073 2071 char *f;
2074 2072 boolean_t defaultaccess = B_TRUE; /* default access is rw */
2075 2073 boolean_t access_ok = B_FALSE;
2076 2074 int count, c;
2077 2075 boolean_t reject = B_FALSE;
2078 2076
2079 2077 opts = strdup(sh->sh_opts);
2080 2078 if (opts == NULL) {
2081 2079 syslog(LOG_ERR, "getclientsflavors: no memory");
2082 2080 return (0);
2083 2081 }
2084 2082
2085 2083 p = opts;
2086 2084 count = c = 0;
2087 2085
2088 2086 while (*p) {
2089 2087 switch (getsubopt(&p, optlist, &val)) {
2090 2088 case OPT_SEC:
2091 2089 if (reject)
2092 2090 access_ok = B_FALSE;
2093 2091
2094 2092 /*
2095 2093 * Before a new sec=xxx option, check if we need
2096 2094 * to move the c index back to the previous count.
2097 2095 */
2098 2096 if (!defaultaccess && !access_ok) {
2099 2097 c = count;
2100 2098 }
2101 2099
2102 2100 /* get all the sec=f1[:f2] flavors */
2103 2101 while ((f = strtok_r(val, ":", &lasts)) != NULL) {
2104 2102 flavors[c++] = map_flavor(f);
2105 2103 val = NULL;
2106 2104 }
2107 2105
2108 2106 /* for a new sec=xxx option, default is rw access */
2109 2107 defaultaccess = B_TRUE;
2110 2108 access_ok = B_FALSE;
2111 2109 reject = B_FALSE;
2112 2110 break;
2113 2111
2114 2112 case OPT_RO:
2115 2113 case OPT_RW:
2116 2114 defaultaccess = B_FALSE;
2117 2115 if (in_access_list(cln, val) > 0)
2118 2116 access_ok = B_TRUE;
2119 2117 break;
2120 2118
2121 2119 case OPT_NONE:
2122 2120 defaultaccess = B_FALSE;
2123 2121 if (in_access_list(cln, val) > 0)
2124 2122 reject = B_TRUE; /* none overides rw/ro */
2125 2123 break;
2126 2124 }
2127 2125 }
2128 2126
2129 2127 if (reject)
2130 2128 access_ok = B_FALSE;
2131 2129
2132 2130 if (!defaultaccess && !access_ok)
2133 2131 c = count;
2134 2132
2135 2133 free(opts);
2136 2134
2137 2135 return (c);
2138 2136 }
2139 2137
2140 2138 /*
2141 2139 * This is a tricky piece of code that parses the
2142 2140 * share options looking for a match on the auth
2143 2141 * flavor that the client is using. If it finds
2144 2142 * a match, then the client is given ro, rw, or
2145 2143 * no access depending whether it is in the access
2146 2144 * list. There is a special case for "secure"
2147 2145 * flavor. Other flavors are values of the new "sec=" option.
2148 2146 */
2149 2147 int
2150 2148 check_client(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
2151 2149 gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2152 2150 gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2153 2151 {
2154 2152 if (newopts(sh->sh_opts))
2155 2153 return (check_client_new(sh, cln, flavor, clnt_uid, clnt_gid,
2156 2154 clnt_ngids, clnt_gids, srv_uid, srv_gid, srv_ngids,
2157 2155 srv_gids));
2158 2156 else
2159 2157 return (check_client_old(sh, cln, flavor, clnt_uid, clnt_gid,
2160 2158 clnt_ngids, clnt_gids, srv_uid, srv_gid, srv_ngids,
2161 2159 srv_gids));
2162 2160 }
2163 2161
2164 2162 extern int _getgroupsbymember(const char *, gid_t[], int, int);
2165 2163
2166 2164 /*
2167 2165 * Get supplemental groups for uid
2168 2166 */
2169 2167 static int
2170 2168 getusergroups(uid_t uid, uint_t *ngrps, gid_t **grps)
2171 2169 {
2172 2170 struct passwd pwd;
2173 2171 char *pwbuf = alloca(pw_size);
2174 2172 gid_t *tmpgrps = alloca(ngroups_max * sizeof (gid_t));
2175 2173 int tmpngrps;
2176 2174
2177 2175 if (getpwuid_r(uid, &pwd, pwbuf, pw_size) == NULL)
2178 2176 return (-1);
2179 2177
2180 2178 tmpgrps[0] = pwd.pw_gid;
2181 2179
2182 2180 tmpngrps = _getgroupsbymember(pwd.pw_name, tmpgrps, ngroups_max, 1);
2183 2181 if (tmpngrps <= 0) {
2184 2182 syslog(LOG_WARNING,
2185 2183 "getusergroups(): Unable to get groups for user %s",
2186 2184 pwd.pw_name);
2187 2185
2188 2186 return (-1);
2189 2187 }
2190 2188
2191 2189 *grps = malloc(tmpngrps * sizeof (gid_t));
2192 2190 if (*grps == NULL) {
2193 2191 syslog(LOG_ERR,
2194 2192 "getusergroups(): Memory allocation failed: %m");
2195 2193
2196 2194 return (-1);
2197 2195 }
2198 2196
2199 2197 *ngrps = tmpngrps;
2200 2198 (void) memcpy(*grps, tmpgrps, tmpngrps * sizeof (gid_t));
2201 2199
2202 2200 return (0);
2203 2201 }
2204 2202
2205 2203 /*
2206 2204 * is_a_number(number)
2207 2205 *
2208 2206 * is the string a number in one of the forms we want to use?
2209 2207 */
2210 2208
2211 2209 static int
2212 2210 is_a_number(char *number)
2213 2211 {
2214 2212 int ret = 1;
2215 2213 int hex = 0;
2216 2214
2217 2215 if (strncmp(number, "0x", 2) == 0) {
2218 2216 number += 2;
2219 2217 hex = 1;
2220 2218 } else if (*number == '-') {
2221 2219 number++; /* skip the minus */
2222 2220 }
2223 2221 while (ret == 1 && *number != '\0') {
2224 2222 if (hex) {
2225 2223 ret = isxdigit(*number++);
2226 2224 } else {
2227 2225 ret = isdigit(*number++);
2228 2226 }
2229 2227 }
2230 2228 return (ret);
2231 2229 }
2232 2230
2233 2231 static boolean_t
2234 2232 get_uid(char *value, uid_t *uid)
2235 2233 {
2236 2234 if (!is_a_number(value)) {
2237 2235 struct passwd *pw;
2238 2236 /*
2239 2237 * in this case it would have to be a
2240 2238 * user name
2241 2239 */
2242 2240 pw = getpwnam(value);
2243 2241 if (pw == NULL)
2244 2242 return (B_FALSE);
2245 2243 *uid = pw->pw_uid;
2246 2244 endpwent();
2247 2245 } else {
2248 2246 uint64_t intval;
2249 2247 intval = strtoull(value, NULL, 0);
2250 2248 if (intval > UID_MAX && intval != -1)
2251 2249 return (B_FALSE);
2252 2250 *uid = (uid_t)intval;
2253 2251 }
2254 2252
2255 2253 return (B_TRUE);
2256 2254 }
2257 2255
2258 2256 static boolean_t
2259 2257 get_gid(char *value, gid_t *gid)
2260 2258 {
2261 2259 if (!is_a_number(value)) {
2262 2260 struct group *gr;
2263 2261 /*
2264 2262 * in this case it would have to be a
2265 2263 * group name
2266 2264 */
2267 2265 gr = getgrnam(value);
2268 2266 if (gr == NULL)
2269 2267 return (B_FALSE);
2270 2268 *gid = gr->gr_gid;
2271 2269 endgrent();
2272 2270 } else {
2273 2271 uint64_t intval;
2274 2272 intval = strtoull(value, NULL, 0);
2275 2273 if (intval > UID_MAX && intval != -1)
2276 2274 return (B_FALSE);
2277 2275 *gid = (gid_t)intval;
2278 2276 }
2279 2277
2280 2278 return (B_TRUE);
2281 2279 }
2282 2280
2283 2281 static int
2284 2282 check_client_old(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
2285 2283 gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2286 2284 gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2287 2285 {
2288 2286 char *opts, *p, *val;
2289 2287 int match; /* Set when a flavor is matched */
2290 2288 int perm = 0; /* Set when "ro", "rw" or "root" is matched */
2291 2289 int list = 0; /* Set when "ro", "rw" is found */
2292 2290 int ro_val = 0; /* Set if ro option is 'ro=' */
2293 2291 int rw_val = 0; /* Set if rw option is 'rw=' */
2294 2292
2295 2293 boolean_t map_deny = B_FALSE;
2296 2294
2297 2295 opts = strdup(sh->sh_opts);
2298 2296 if (opts == NULL) {
2299 2297 syslog(LOG_ERR, "check_client: no memory");
2300 2298 return (0);
2301 2299 }
2302 2300
2303 2301 /*
2304 2302 * If client provided 16 supplemental groups with AUTH_SYS, lookup
2305 2303 * locally for all of them
2306 2304 */
2307 2305 if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS)
2308 2306 if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0)
2309 2307 perm |= NFSAUTH_GROUPS;
2310 2308
2311 2309 p = opts;
2312 2310 match = AUTH_UNIX;
2313 2311
2314 2312 while (*p) {
2315 2313 switch (getsubopt(&p, optlist, &val)) {
2316 2314
2317 2315 case OPT_SECURE:
2318 2316 match = AUTH_DES;
2319 2317
2320 2318 if (perm & NFSAUTH_GROUPS) {
2321 2319 free(*srv_gids);
2322 2320 *srv_ngids = 0;
2323 2321 *srv_gids = NULL;
2324 2322 perm &= ~NFSAUTH_GROUPS;
2325 2323 }
2326 2324
2327 2325 break;
2328 2326
2329 2327 case OPT_RO:
2330 2328 list++;
2331 2329 if (val != NULL)
2332 2330 ro_val++;
2333 2331 if (in_access_list(cln, val) > 0)
2334 2332 perm |= NFSAUTH_RO;
2335 2333 break;
2336 2334
2337 2335 case OPT_RW:
2338 2336 list++;
2339 2337 if (val != NULL)
2340 2338 rw_val++;
2341 2339 if (in_access_list(cln, val) > 0)
2342 2340 perm |= NFSAUTH_RW;
2343 2341 break;
2344 2342
2345 2343 case OPT_ROOT:
2346 2344 /*
2347 2345 * Check if the client is in
2348 2346 * the root list. Only valid
2349 2347 * for AUTH_SYS.
2350 2348 */
2351 2349 if (flavor != AUTH_SYS)
2352 2350 break;
2353 2351
2354 2352 if (val == NULL || *val == '\0')
2355 2353 break;
2356 2354
2357 2355 if (clnt_uid != 0)
2358 2356 break;
2359 2357
2360 2358 if (in_access_list(cln, val) > 0) {
2361 2359 perm |= NFSAUTH_ROOT;
2362 2360 perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP;
2363 2361 map_deny = B_FALSE;
2364 2362
2365 2363 if (perm & NFSAUTH_GROUPS) {
2366 2364 free(*srv_gids);
2367 2365 *srv_ngids = 0;
2368 2366 *srv_gids = NULL;
2369 2367 perm &= ~NFSAUTH_GROUPS;
2370 2368 }
2371 2369 }
2372 2370 break;
2373 2371
2374 2372 case OPT_NONE:
2375 2373 /*
2376 2374 * Check if the client should have no access
2377 2375 * to this share at all. This option behaves
2378 2376 * more like "root" than either "rw" or "ro".
2379 2377 */
2380 2378 if (in_access_list(cln, val) > 0)
2381 2379 perm |= NFSAUTH_DENIED;
2382 2380 break;
2383 2381
2384 2382 case OPT_UIDMAP: {
2385 2383 char *c;
2386 2384 char *n;
2387 2385
2388 2386 /*
2389 2387 * The uidmap is supported for AUTH_SYS only.
2390 2388 */
2391 2389 if (flavor != AUTH_SYS)
2392 2390 break;
2393 2391
2394 2392 if (perm & NFSAUTH_UIDMAP || map_deny)
2395 2393 break;
2396 2394
2397 2395 for (c = val; c != NULL; c = n) {
2398 2396 char *s;
2399 2397 char *al;
2400 2398 uid_t srv;
2401 2399
2402 2400 n = strchr(c, '~');
2403 2401 if (n != NULL)
2404 2402 *n++ = '\0';
2405 2403
2406 2404 s = strchr(c, ':');
2407 2405 if (s != NULL) {
2408 2406 *s++ = '\0';
2409 2407 al = strchr(s, ':');
2410 2408 if (al != NULL)
2411 2409 *al++ = '\0';
2412 2410 }
2413 2411
2414 2412 if (s == NULL || al == NULL)
2415 2413 continue;
2416 2414
2417 2415 if (*c == '\0') {
2418 2416 if (clnt_uid != (uid_t)-1)
2419 2417 continue;
2420 2418 } else if (strcmp(c, "*") != 0) {
2421 2419 uid_t clnt;
2422 2420
2423 2421 if (!get_uid(c, &clnt))
2424 2422 continue;
2425 2423
2426 2424 if (clnt_uid != clnt)
2427 2425 continue;
2428 2426 }
2429 2427
2430 2428 if (*s == '\0')
2431 2429 srv = UID_NOBODY;
2432 2430 else if (!get_uid(s, &srv))
2433 2431 continue;
2434 2432 else if (srv == (uid_t)-1) {
2435 2433 map_deny = B_TRUE;
2436 2434 break;
2437 2435 }
2438 2436
2439 2437 if (in_access_list(cln, al) > 0) {
2440 2438 *srv_uid = srv;
2441 2439 perm |= NFSAUTH_UIDMAP;
2442 2440
2443 2441 if (perm & NFSAUTH_GROUPS) {
2444 2442 free(*srv_gids);
2445 2443 *srv_ngids = 0;
2446 2444 *srv_gids = NULL;
2447 2445 perm &= ~NFSAUTH_GROUPS;
2448 2446 }
2449 2447
2450 2448 break;
2451 2449 }
2452 2450 }
2453 2451
2454 2452 break;
2455 2453 }
2456 2454
2457 2455 case OPT_GIDMAP: {
2458 2456 char *c;
2459 2457 char *n;
2460 2458
2461 2459 /*
2462 2460 * The gidmap is supported for AUTH_SYS only.
2463 2461 */
2464 2462 if (flavor != AUTH_SYS)
2465 2463 break;
2466 2464
2467 2465 if (perm & NFSAUTH_GIDMAP || map_deny)
2468 2466 break;
2469 2467
2470 2468 for (c = val; c != NULL; c = n) {
2471 2469 char *s;
2472 2470 char *al;
2473 2471 gid_t srv;
2474 2472
2475 2473 n = strchr(c, '~');
2476 2474 if (n != NULL)
2477 2475 *n++ = '\0';
2478 2476
2479 2477 s = strchr(c, ':');
2480 2478 if (s != NULL) {
2481 2479 *s++ = '\0';
2482 2480 al = strchr(s, ':');
2483 2481 if (al != NULL)
2484 2482 *al++ = '\0';
2485 2483 }
2486 2484
2487 2485 if (s == NULL || al == NULL)
2488 2486 break;
2489 2487
2490 2488 if (*c == '\0') {
2491 2489 if (clnt_gid != (gid_t)-1)
2492 2490 continue;
2493 2491 } else if (strcmp(c, "*") != 0) {
2494 2492 gid_t clnt;
2495 2493
2496 2494 if (!get_gid(c, &clnt))
2497 2495 continue;
2498 2496
2499 2497 if (clnt_gid != clnt)
2500 2498 continue;
2501 2499 }
2502 2500
2503 2501 if (*s == '\0')
2504 2502 srv = UID_NOBODY;
2505 2503 else if (!get_gid(s, &srv))
2506 2504 continue;
2507 2505 else if (srv == (gid_t)-1) {
2508 2506 map_deny = B_TRUE;
2509 2507 break;
2510 2508 }
2511 2509
2512 2510 if (in_access_list(cln, al) > 0) {
2513 2511 *srv_gid = srv;
2514 2512 perm |= NFSAUTH_GIDMAP;
2515 2513
2516 2514 if (perm & NFSAUTH_GROUPS) {
2517 2515 free(*srv_gids);
2518 2516 *srv_ngids = 0;
2519 2517 *srv_gids = NULL;
2520 2518 perm &= ~NFSAUTH_GROUPS;
2521 2519 }
2522 2520
2523 2521 break;
2524 2522 }
2525 2523 }
2526 2524
2527 2525 break;
2528 2526 }
2529 2527
2530 2528 default:
2531 2529 break;
2532 2530 }
2533 2531 }
2534 2532
2535 2533 free(opts);
2536 2534
2537 2535 if (perm & NFSAUTH_ROOT) {
2538 2536 *srv_uid = 0;
2539 2537 *srv_gid = 0;
2540 2538 }
2541 2539
2542 2540 if (map_deny)
2543 2541 perm |= NFSAUTH_DENIED;
2544 2542
2545 2543 if (!(perm & NFSAUTH_UIDMAP))
2546 2544 *srv_uid = clnt_uid;
2547 2545 if (!(perm & NFSAUTH_GIDMAP))
2548 2546 *srv_gid = clnt_gid;
2549 2547
2550 2548 if (flavor != match || perm & NFSAUTH_DENIED)
2551 2549 return (NFSAUTH_DENIED);
2552 2550
2553 2551 if (list) {
2554 2552 /*
2555 2553 * If the client doesn't match an "ro" or "rw"
2556 2554 * list then set no access.
2557 2555 */
2558 2556 if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0)
2559 2557 perm |= NFSAUTH_DENIED;
2560 2558 } else {
2561 2559 /*
2562 2560 * The client matched a flavor entry that
2563 2561 * has no explicit "rw" or "ro" determination.
2564 2562 * Default it to "rw".
2565 2563 */
2566 2564 perm |= NFSAUTH_RW;
2567 2565 }
2568 2566
2569 2567 /*
2570 2568 * The client may show up in both ro= and rw=
2571 2569 * lists. If so, then turn off the RO access
2572 2570 * bit leaving RW access.
2573 2571 */
2574 2572 if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
2575 2573 /*
2576 2574 * Logically cover all permutations of rw=,ro=.
2577 2575 * In the case where, rw,ro=<host> we would like
2578 2576 * to remove RW access for the host. In all other cases
2579 2577 * RW wins the precedence battle.
2580 2578 */
2581 2579 if (!rw_val && ro_val) {
2582 2580 perm &= ~(NFSAUTH_RW);
2583 2581 } else {
2584 2582 perm &= ~(NFSAUTH_RO);
2585 2583 }
2586 2584 }
2587 2585
2588 2586 return (perm);
2589 2587 }
2590 2588
2591 2589 /*
2592 2590 * Check if the client has access by using a flavor different from
2593 2591 * the given "flavor". If "flavor" is not in the flavor list,
2594 2592 * return TRUE to indicate that this "flavor" is a wrong sec.
2595 2593 */
2596 2594 static bool_t
2597 2595 is_wrongsec(share_t *sh, struct cln *cln, int flavor)
2598 2596 {
2599 2597 int flavor_list[MAX_FLAVORS];
2600 2598 int flavor_count, i;
2601 2599
2602 2600 /* get the flavor list that the client has access with */
2603 2601 flavor_count = getclientsflavors_new(sh, cln, flavor_list);
2604 2602
2605 2603 if (flavor_count == 0)
2606 2604 return (FALSE);
2607 2605
2608 2606 /*
2609 2607 * Check if the given "flavor" is in the flavor_list.
2610 2608 */
2611 2609 for (i = 0; i < flavor_count; i++) {
2612 2610 if (flavor == flavor_list[i])
2613 2611 return (FALSE);
2614 2612 }
2615 2613
2616 2614 /*
2617 2615 * If "flavor" is not in the flavor_list, return TRUE to indicate
2618 2616 * that the client should have access by using a security flavor
2619 2617 * different from this "flavor".
2620 2618 */
2621 2619 return (TRUE);
2622 2620 }
2623 2621
2624 2622 /*
2625 2623 * Given an export and the client's hostname, we
2626 2624 * check the security options to see whether the
2627 2625 * client is allowed to use the given security flavor.
2628 2626 *
2629 2627 * The strategy is to proceed through the options looking
2630 2628 * for a flavor match, then pay attention to the ro, rw,
2631 2629 * and root options.
2632 2630 *
2633 2631 * Note that an entry may list several flavors in a
2634 2632 * single entry, e.g.
2635 2633 *
2636 2634 * sec=krb5,rw=clnt1:clnt2,ro,sec=sys,ro
2637 2635 *
2638 2636 */
2639 2637
2640 2638 static int
2641 2639 check_client_new(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
2642 2640 gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2643 2641 gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2644 2642 {
2645 2643 char *opts, *p, *val;
2646 2644 char *lasts;
2647 2645 char *f;
2648 2646 int match = 0; /* Set when a flavor is matched */
2649 2647 int perm = 0; /* Set when "ro", "rw" or "root" is matched */
2650 2648 int list = 0; /* Set when "ro", "rw" is found */
2651 2649 int ro_val = 0; /* Set if ro option is 'ro=' */
2652 2650 int rw_val = 0; /* Set if rw option is 'rw=' */
2653 2651
2654 2652 boolean_t map_deny = B_FALSE;
2655 2653
2656 2654 opts = strdup(sh->sh_opts);
2657 2655 if (opts == NULL) {
2658 2656 syslog(LOG_ERR, "check_client: no memory");
2659 2657 return (0);
2660 2658 }
2661 2659
2662 2660 /*
2663 2661 * If client provided 16 supplemental groups with AUTH_SYS, lookup
2664 2662 * locally for all of them
2665 2663 */
2666 2664 if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS)
2667 2665 if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0)
2668 2666 perm |= NFSAUTH_GROUPS;
2669 2667
2670 2668 p = opts;
2671 2669
2672 2670 while (*p) {
2673 2671 switch (getsubopt(&p, optlist, &val)) {
2674 2672
2675 2673 case OPT_SEC:
2676 2674 if (match)
2677 2675 goto done;
2678 2676
2679 2677 while ((f = strtok_r(val, ":", &lasts))
2680 2678 != NULL) {
2681 2679 if (flavor == map_flavor(f)) {
2682 2680 match = 1;
2683 2681 break;
2684 2682 }
2685 2683 val = NULL;
2686 2684 }
2687 2685 break;
2688 2686
2689 2687 case OPT_RO:
2690 2688 if (!match)
2691 2689 break;
2692 2690
2693 2691 list++;
2694 2692 if (val != NULL)
2695 2693 ro_val++;
2696 2694 if (in_access_list(cln, val) > 0)
2697 2695 perm |= NFSAUTH_RO;
2698 2696 break;
2699 2697
2700 2698 case OPT_RW:
2701 2699 if (!match)
2702 2700 break;
2703 2701
2704 2702 list++;
2705 2703 if (val != NULL)
2706 2704 rw_val++;
2707 2705 if (in_access_list(cln, val) > 0)
2708 2706 perm |= NFSAUTH_RW;
2709 2707 break;
2710 2708
2711 2709 case OPT_ROOT:
2712 2710 /*
2713 2711 * Check if the client is in
2714 2712 * the root list. Only valid
2715 2713 * for AUTH_SYS.
2716 2714 */
2717 2715 if (flavor != AUTH_SYS)
2718 2716 break;
2719 2717
2720 2718 if (!match)
2721 2719 break;
2722 2720
2723 2721 if (val == NULL || *val == '\0')
2724 2722 break;
2725 2723
2726 2724 if (clnt_uid != 0)
2727 2725 break;
2728 2726
2729 2727 if (in_access_list(cln, val) > 0) {
2730 2728 perm |= NFSAUTH_ROOT;
2731 2729 perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP;
2732 2730 map_deny = B_FALSE;
2733 2731
2734 2732 if (perm & NFSAUTH_GROUPS) {
2735 2733 free(*srv_gids);
2736 2734 *srv_gids = NULL;
2737 2735 *srv_ngids = 0;
2738 2736 perm &= ~NFSAUTH_GROUPS;
2739 2737 }
2740 2738 }
2741 2739 break;
2742 2740
2743 2741 case OPT_NONE:
2744 2742 /*
2745 2743 * Check if the client should have no access
2746 2744 * to this share at all. This option behaves
2747 2745 * more like "root" than either "rw" or "ro".
2748 2746 */
2749 2747 if (in_access_list(cln, val) > 0)
2750 2748 perm |= NFSAUTH_DENIED;
2751 2749 break;
2752 2750
2753 2751 case OPT_UIDMAP: {
2754 2752 char *c;
2755 2753 char *n;
2756 2754
2757 2755 /*
2758 2756 * The uidmap is supported for AUTH_SYS only.
2759 2757 */
2760 2758 if (flavor != AUTH_SYS)
2761 2759 break;
2762 2760
2763 2761 if (!match || perm & NFSAUTH_UIDMAP || map_deny)
2764 2762 break;
2765 2763
2766 2764 for (c = val; c != NULL; c = n) {
2767 2765 char *s;
2768 2766 char *al;
2769 2767 uid_t srv;
2770 2768
2771 2769 n = strchr(c, '~');
2772 2770 if (n != NULL)
2773 2771 *n++ = '\0';
2774 2772
2775 2773 s = strchr(c, ':');
2776 2774 if (s != NULL) {
2777 2775 *s++ = '\0';
2778 2776 al = strchr(s, ':');
2779 2777 if (al != NULL)
2780 2778 *al++ = '\0';
2781 2779 }
2782 2780
2783 2781 if (s == NULL || al == NULL)
2784 2782 continue;
2785 2783
2786 2784 if (*c == '\0') {
2787 2785 if (clnt_uid != (uid_t)-1)
2788 2786 continue;
2789 2787 } else if (strcmp(c, "*") != 0) {
2790 2788 uid_t clnt;
2791 2789
2792 2790 if (!get_uid(c, &clnt))
2793 2791 continue;
2794 2792
2795 2793 if (clnt_uid != clnt)
2796 2794 continue;
2797 2795 }
2798 2796
2799 2797 if (*s == '\0')
2800 2798 srv = UID_NOBODY;
2801 2799 else if (!get_uid(s, &srv))
2802 2800 continue;
2803 2801 else if (srv == (uid_t)-1) {
2804 2802 map_deny = B_TRUE;
2805 2803 break;
2806 2804 }
2807 2805
2808 2806 if (in_access_list(cln, al) > 0) {
2809 2807 *srv_uid = srv;
2810 2808 perm |= NFSAUTH_UIDMAP;
2811 2809
2812 2810 if (perm & NFSAUTH_GROUPS) {
2813 2811 free(*srv_gids);
2814 2812 *srv_gids = NULL;
2815 2813 *srv_ngids = 0;
2816 2814 perm &= ~NFSAUTH_GROUPS;
2817 2815 }
2818 2816
2819 2817 break;
2820 2818 }
2821 2819 }
2822 2820
2823 2821 break;
2824 2822 }
2825 2823
2826 2824 case OPT_GIDMAP: {
2827 2825 char *c;
2828 2826 char *n;
2829 2827
2830 2828 /*
2831 2829 * The gidmap is supported for AUTH_SYS only.
2832 2830 */
2833 2831 if (flavor != AUTH_SYS)
2834 2832 break;
2835 2833
2836 2834 if (!match || perm & NFSAUTH_GIDMAP || map_deny)
2837 2835 break;
2838 2836
2839 2837 for (c = val; c != NULL; c = n) {
2840 2838 char *s;
2841 2839 char *al;
2842 2840 gid_t srv;
2843 2841
2844 2842 n = strchr(c, '~');
2845 2843 if (n != NULL)
2846 2844 *n++ = '\0';
2847 2845
2848 2846 s = strchr(c, ':');
2849 2847 if (s != NULL) {
2850 2848 *s++ = '\0';
2851 2849 al = strchr(s, ':');
2852 2850 if (al != NULL)
2853 2851 *al++ = '\0';
2854 2852 }
2855 2853
2856 2854 if (s == NULL || al == NULL)
2857 2855 break;
2858 2856
2859 2857 if (*c == '\0') {
2860 2858 if (clnt_gid != (gid_t)-1)
2861 2859 continue;
2862 2860 } else if (strcmp(c, "*") != 0) {
2863 2861 gid_t clnt;
2864 2862
2865 2863 if (!get_gid(c, &clnt))
2866 2864 continue;
2867 2865
2868 2866 if (clnt_gid != clnt)
2869 2867 continue;
2870 2868 }
2871 2869
2872 2870 if (*s == '\0')
2873 2871 srv = UID_NOBODY;
2874 2872 else if (!get_gid(s, &srv))
2875 2873 continue;
2876 2874 else if (srv == (gid_t)-1) {
2877 2875 map_deny = B_TRUE;
2878 2876 break;
2879 2877 }
2880 2878
2881 2879 if (in_access_list(cln, al) > 0) {
2882 2880 *srv_gid = srv;
2883 2881 perm |= NFSAUTH_GIDMAP;
2884 2882
2885 2883 if (perm & NFSAUTH_GROUPS) {
2886 2884 free(*srv_gids);
2887 2885 *srv_gids = NULL;
2888 2886 *srv_ngids = 0;
2889 2887 perm &= ~NFSAUTH_GROUPS;
2890 2888 }
2891 2889
2892 2890 break;
2893 2891 }
2894 2892 }
2895 2893
2896 2894 break;
2897 2895 }
2898 2896
2899 2897 default:
2900 2898 break;
2901 2899 }
2902 2900 }
2903 2901
2904 2902 done:
2905 2903 if (perm & NFSAUTH_ROOT) {
2906 2904 *srv_uid = 0;
2907 2905 *srv_gid = 0;
2908 2906 }
2909 2907
2910 2908 if (map_deny)
2911 2909 perm |= NFSAUTH_DENIED;
2912 2910
2913 2911 if (!(perm & NFSAUTH_UIDMAP))
2914 2912 *srv_uid = clnt_uid;
2915 2913 if (!(perm & NFSAUTH_GIDMAP))
2916 2914 *srv_gid = clnt_gid;
2917 2915
2918 2916 /*
2919 2917 * If no match then set the perm accordingly
2920 2918 */
2921 2919 if (!match || perm & NFSAUTH_DENIED) {
2922 2920 free(opts);
2923 2921 return (NFSAUTH_DENIED);
2924 2922 }
2925 2923
2926 2924 if (list) {
2927 2925 /*
2928 2926 * If the client doesn't match an "ro" or "rw" list then
2929 2927 * check if it may have access by using a different flavor.
2930 2928 * If so, return NFSAUTH_WRONGSEC.
2931 2929 * If not, return NFSAUTH_DENIED.
2932 2930 */
2933 2931 if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0) {
2934 2932 if (is_wrongsec(sh, cln, flavor))
2935 2933 perm |= NFSAUTH_WRONGSEC;
2936 2934 else
2937 2935 perm |= NFSAUTH_DENIED;
2938 2936 }
2939 2937 } else {
2940 2938 /*
2941 2939 * The client matched a flavor entry that
2942 2940 * has no explicit "rw" or "ro" determination.
2943 2941 * Make sure it defaults to "rw".
2944 2942 */
2945 2943 perm |= NFSAUTH_RW;
2946 2944 }
2947 2945
2948 2946 /*
2949 2947 * The client may show up in both ro= and rw=
2950 2948 * lists. If so, then turn off the RO access
2951 2949 * bit leaving RW access.
2952 2950 */
2953 2951 if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
2954 2952 /*
2955 2953 * Logically cover all permutations of rw=,ro=.
2956 2954 * In the case where, rw,ro=<host> we would like
2957 2955 * to remove RW access for the host. In all other cases
2958 2956 * RW wins the precedence battle.
2959 2957 */
2960 2958 if (!rw_val && ro_val) {
2961 2959 perm &= ~(NFSAUTH_RW);
2962 2960 } else {
2963 2961 perm &= ~(NFSAUTH_RO);
2964 2962 }
2965 2963 }
2966 2964
2967 2965 free(opts);
2968 2966
2969 2967 return (perm);
2970 2968 }
2971 2969
2972 2970 void
2973 2971 check_sharetab()
2974 2972 {
2975 2973 FILE *f;
2976 2974 struct stat st;
2977 2975 static timestruc_t last_sharetab_time;
2978 2976 timestruc_t prev_sharetab_time;
2979 2977 share_t *sh;
2980 2978 struct sh_list *shp, *shp_prev;
2981 2979 int res, c = 0;
2982 2980
2983 2981 /*
2984 2982 * read in /etc/dfs/sharetab if it has changed
2985 2983 */
2986 2984 if (stat(SHARETAB, &st) != 0) {
2987 2985 syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
2988 2986 return;
2989 2987 }
2990 2988
2991 2989 if (st.st_mtim.tv_sec == last_sharetab_time.tv_sec &&
2992 2990 st.st_mtim.tv_nsec == last_sharetab_time.tv_nsec) {
2993 2991 /*
2994 2992 * No change.
2995 2993 */
2996 2994 return;
2997 2995 }
2998 2996
2999 2997 /*
3000 2998 * Remember the mod time, then after getting the
3001 2999 * write lock check again. If another thread
3002 3000 * already did the update, then there's no
3003 3001 * work to do.
3004 3002 */
3005 3003 prev_sharetab_time = last_sharetab_time;
3006 3004
3007 3005 (void) rw_wrlock(&sharetab_lock);
3008 3006
3009 3007 if (prev_sharetab_time.tv_sec != last_sharetab_time.tv_sec ||
3010 3008 prev_sharetab_time.tv_nsec != last_sharetab_time.tv_nsec) {
3011 3009 (void) rw_unlock(&sharetab_lock);
3012 3010 return;
3013 3011 }
3014 3012
3015 3013 /*
3016 3014 * Note that since the sharetab is now in memory
3017 3015 * and a snapshot is taken, we no longer have to
3018 3016 * lock the file.
3019 3017 */
3020 3018 f = fopen(SHARETAB, "r");
3021 3019 if (f == NULL) {
3022 3020 syslog(LOG_ERR, "Cannot open %s: %m", SHARETAB);
3023 3021 (void) rw_unlock(&sharetab_lock);
3024 3022 return;
3025 3023 }
3026 3024
3027 3025 /*
3028 3026 * Once we are sure /etc/dfs/sharetab has been
3029 3027 * modified, flush netgroup cache entries.
3030 3028 */
3031 3029 netgrp_cache_flush();
3032 3030
3033 3031 sh_free(share_list); /* free old list */
3034 3032 share_list = NULL;
3035 3033
3036 3034 while ((res = getshare(f, &sh)) > 0) {
3037 3035 c++;
3038 3036 if (strcmp(sh->sh_fstype, "nfs") != 0)
3039 3037 continue;
3040 3038
3041 3039 shp = malloc(sizeof (*shp));
3042 3040 if (shp == NULL)
3043 3041 goto alloc_failed;
3044 3042 if (share_list == NULL)
3045 3043 share_list = shp;
3046 3044 else
3047 3045 /* LINTED not used before set */
3048 3046 shp_prev->shl_next = shp;
3049 3047 shp_prev = shp;
3050 3048 shp->shl_next = NULL;
3051 3049 shp->shl_sh = sharedup(sh);
3052 3050 if (shp->shl_sh == NULL)
3053 3051 goto alloc_failed;
3054 3052 }
3055 3053
3056 3054 if (res < 0)
3057 3055 syslog(LOG_ERR, "%s: invalid at line %d\n",
3058 3056 SHARETAB, c + 1);
3059 3057
3060 3058 if (stat(SHARETAB, &st) != 0) {
3061 3059 syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
3062 3060 (void) fclose(f);
3063 3061 (void) rw_unlock(&sharetab_lock);
3064 3062 return;
3065 3063 }
3066 3064
3067 3065 last_sharetab_time = st.st_mtim;
3068 3066 (void) fclose(f);
3069 3067 (void) rw_unlock(&sharetab_lock);
3070 3068
3071 3069 return;
3072 3070
3073 3071 alloc_failed:
3074 3072
3075 3073 syslog(LOG_ERR, "check_sharetab: no memory");
3076 3074 sh_free(share_list);
3077 3075 share_list = NULL;
3078 3076 (void) fclose(f);
3079 3077 (void) rw_unlock(&sharetab_lock);
3080 3078 }
3081 3079
3082 3080 static void
3083 3081 sh_free(struct sh_list *shp)
3084 3082 {
3085 3083 struct sh_list *next;
3086 3084
3087 3085 while (shp) {
3088 3086 sharefree(shp->shl_sh);
3089 3087 next = shp->shl_next;
3090 3088 free(shp);
3091 3089 shp = next;
3092 3090 }
3093 3091 }
3094 3092
3095 3093
3096 3094 /*
3097 3095 * Remove an entry from mounted list
3098 3096 */
3099 3097 static void
3100 3098 umount(struct svc_req *rqstp)
3101 3099 {
3102 3100 char *host, *path, *remove_path;
3103 3101 char rpath[MAXPATHLEN];
3104 3102 SVCXPRT *transp;
3105 3103 struct cln cln;
3106 3104
3107 3105 transp = rqstp->rq_xprt;
3108 3106 path = NULL;
3109 3107 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
3110 3108 svcerr_decode(transp);
3111 3109 return;
3112 3110 }
3113 3111
3114 3112 cln_init(&cln, transp);
3115 3113
3116 3114 errno = 0;
3117 3115 if (!svc_sendreply(transp, xdr_void, (char *)NULL))
3118 3116 log_cant_reply_cln(&cln);
3119 3117
3120 3118 host = cln_gethost(&cln);
3121 3119 if (host == NULL) {
3122 3120 /*
3123 3121 * Without the hostname we can't do audit or delete
3124 3122 * this host from the mount entries.
3125 3123 */
3126 3124 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
3127 3125 return;
3128 3126 }
3129 3127
3130 3128 if (verbose)
3131 3129 syslog(LOG_NOTICE, "UNMOUNT: %s unmounted %s", host, path);
3132 3130
3133 3131 audit_mountd_umount(host, path);
3134 3132
3135 3133 remove_path = rpath; /* assume we will use the cannonical path */
3136 3134 if (realpath(path, rpath) == NULL) {
3137 3135 if (verbose)
3138 3136 syslog(LOG_WARNING, "UNMOUNT: realpath: %s: %m ", path);
3139 3137 remove_path = path; /* use path provided instead */
3140 3138 }
3141 3139
3142 3140 mntlist_delete(host, remove_path); /* remove from mount list */
3143 3141
3144 3142 cln_fini(&cln);
3145 3143
3146 3144 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
3147 3145 }
3148 3146
3149 3147 /*
3150 3148 * Remove all entries for one machine from mounted list
3151 3149 */
3152 3150 static void
3153 3151 umountall(struct svc_req *rqstp)
3154 3152 {
3155 3153 SVCXPRT *transp;
3156 3154 char *host;
3157 3155 struct cln cln;
3158 3156
3159 3157 transp = rqstp->rq_xprt;
3160 3158 if (!svc_getargs(transp, xdr_void, NULL)) {
3161 3159 svcerr_decode(transp);
3162 3160 return;
3163 3161 }
3164 3162 /*
3165 3163 * We assume that this call is asynchronous and made via rpcbind
3166 3164 * callit routine. Therefore return control immediately. The error
3167 3165 * causes rpcbind to remain silent, as opposed to every machine
3168 3166 * on the net blasting the requester with a response.
3169 3167 */
3170 3168 svcerr_systemerr(transp);
3171 3169
3172 3170 cln_init(&cln, transp);
3173 3171
3174 3172 host = cln_gethost(&cln);
3175 3173 if (host == NULL) {
3176 3174 /* Can't do anything without the name of the client */
3177 3175 return;
3178 3176 }
3179 3177
3180 3178 /*
3181 3179 * Remove all hosts entries from mount list
3182 3180 */
3183 3181 mntlist_delete_all(host);
3184 3182
3185 3183 if (verbose)
3186 3184 syslog(LOG_NOTICE, "UNMOUNTALL: from %s", host);
3187 3185
3188 3186 cln_fini(&cln);
3189 3187 }
3190 3188
3191 3189 void *
3192 3190 exmalloc(size_t size)
3193 3191 {
3194 3192 void *ret;
3195 3193
3196 3194 if ((ret = malloc(size)) == NULL) {
3197 3195 syslog(LOG_ERR, "Out of memory");
3198 3196 exit(1);
3199 3197 }
3200 3198 return (ret);
3201 3199 }
3202 3200
3203 3201 static tsol_tpent_t *
3204 3202 get_client_template(struct sockaddr *sock)
3205 3203 {
3206 3204 in_addr_t v4client;
3207 3205 in6_addr_t v6client;
3208 3206 char v4_addr[INET_ADDRSTRLEN];
3209 3207 char v6_addr[INET6_ADDRSTRLEN];
3210 3208 tsol_rhent_t *rh;
3211 3209 tsol_tpent_t *tp;
3212 3210
3213 3211 switch (sock->sa_family) {
3214 3212 case AF_INET:
3215 3213 v4client = ((struct sockaddr_in *)(void *)sock)->
3216 3214 sin_addr.s_addr;
3217 3215 if (inet_ntop(AF_INET, &v4client, v4_addr, INET_ADDRSTRLEN) ==
3218 3216 NULL)
3219 3217 return (NULL);
3220 3218 rh = tsol_getrhbyaddr(v4_addr, sizeof (v4_addr), AF_INET);
3221 3219 if (rh == NULL)
3222 3220 return (NULL);
3223 3221 tp = tsol_gettpbyname(rh->rh_template);
3224 3222 tsol_freerhent(rh);
3225 3223 return (tp);
3226 3224 break;
3227 3225 case AF_INET6:
3228 3226 v6client = ((struct sockaddr_in6 *)(void *)sock)->sin6_addr;
3229 3227 if (inet_ntop(AF_INET6, &v6client, v6_addr, INET6_ADDRSTRLEN) ==
3230 3228 NULL)
3231 3229 return (NULL);
3232 3230 rh = tsol_getrhbyaddr(v6_addr, sizeof (v6_addr), AF_INET6);
3233 3231 if (rh == NULL)
3234 3232 return (NULL);
3235 3233 tp = tsol_gettpbyname(rh->rh_template);
3236 3234 tsol_freerhent(rh);
3237 3235 return (tp);
3238 3236 break;
3239 3237 default:
3240 3238 return (NULL);
3241 3239 }
3242 3240 }
↓ open down ↓ |
1697 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX