1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * NFS specific functions
28 */
29 #include <stdio.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <zone.h>
35 #include <errno.h>
36 #include <locale.h>
37 #include <signal.h>
38 #include <strings.h>
39 #include "libshare.h"
40 #include "libshare_impl.h"
41 #include <nfs/export.h>
42 #include <pwd.h>
43 #include <limits.h>
44 #include <libscf.h>
45 #include <syslog.h>
46 #include <rpcsvc/daemon_utils.h>
47 #include "nfslog_config.h"
48 #include "nfslogtab.h"
49 #include "libshare_nfs.h"
50 #include <nfs/nfs.h>
51 #include <nfs/nfssys.h>
52 #include "smfcfg.h"
53
54 /* should really be in some global place */
55 #define DEF_WIN 30000
56 #define OPT_CHUNK 1024
57
58 int debug = 0;
59
60 #define NFS_SERVER_SVC "svc:/network/nfs/server:default"
61 #define NFS_CLIENT_SVC (char *)"svc:/network/nfs/client:default"
62
63 /* internal functions */
64 static int nfs_init();
65 static void nfs_fini();
66 static int nfs_enable_share(sa_share_t);
67 static int nfs_disable_share(sa_share_t, char *);
68 static int nfs_validate_property(sa_handle_t, sa_property_t, sa_optionset_t);
69 static int nfs_validate_security_mode(char *);
70 static int nfs_is_security_opt(char *);
71 static int nfs_parse_legacy_options(sa_group_t, char *);
72 static char *nfs_format_options(sa_group_t, int);
73 static int nfs_set_proto_prop(sa_property_t);
74 static sa_protocol_properties_t nfs_get_proto_set();
75 static char *nfs_get_status();
76 static char *nfs_space_alias(char *);
77 static uint64_t nfs_features();
78
79 /*
80 * ops vector that provides the protocol specific info and operations
81 * for share management.
82 */
83
84 struct sa_plugin_ops sa_plugin_ops = {
85 SA_PLUGIN_VERSION,
86 "nfs",
87 nfs_init,
88 nfs_fini,
89 nfs_enable_share,
90 nfs_disable_share,
91 nfs_validate_property,
92 nfs_validate_security_mode,
93 nfs_is_security_opt,
94 nfs_parse_legacy_options,
95 nfs_format_options,
96 nfs_set_proto_prop,
97 nfs_get_proto_set,
98 nfs_get_status,
99 nfs_space_alias,
100 NULL, /* update_legacy */
101 NULL, /* delete_legacy */
102 NULL, /* change_notify */
103 NULL, /* enable_resource */
104 NULL, /* disable_resource */
105 nfs_features,
106 NULL, /* transient shares */
107 NULL, /* notify resource */
108 NULL, /* rename_resource */
109 NULL, /* run_command */
110 NULL, /* command_help */
111 NULL /* delete_proto_section */
112 };
113
114 /*
115 * list of support services needed
116 * defines should come from head/rpcsvc/daemon_utils.h
117 */
118
119 static char *service_list_default[] =
120 { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, REPARSED, NULL };
121 static char *service_list_logging[] =
122 { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NFSLOGD, REPARSED,
123 NULL };
124
125 /*
126 * option definitions. Make sure to keep the #define for the option
127 * index just before the entry it is the index for. Changing the order
128 * can cause breakage. E.g OPT_RW is index 1 and must precede the
129 * line that includes the SHOPT_RW and OPT_RW entries.
130 */
131
132 struct option_defs optdefs[] = {
133 #define OPT_RO 0
134 {SHOPT_RO, OPT_RO, OPT_TYPE_ACCLIST},
135 #define OPT_RW 1
136 {SHOPT_RW, OPT_RW, OPT_TYPE_ACCLIST},
137 #define OPT_ROOT 2
138 {SHOPT_ROOT, OPT_ROOT, OPT_TYPE_ACCLIST},
139 #define OPT_SECURE 3
140 {SHOPT_SECURE, OPT_SECURE, OPT_TYPE_DEPRECATED},
141 #define OPT_ANON 4
142 {SHOPT_ANON, OPT_ANON, OPT_TYPE_USER},
143 #define OPT_WINDOW 5
144 {SHOPT_WINDOW, OPT_WINDOW, OPT_TYPE_NUMBER},
145 #define OPT_NOSUID 6
146 {SHOPT_NOSUID, OPT_NOSUID, OPT_TYPE_BOOLEAN},
147 #define OPT_ACLOK 7
148 {SHOPT_ACLOK, OPT_ACLOK, OPT_TYPE_BOOLEAN},
149 #define OPT_NOSUB 8
150 {SHOPT_NOSUB, OPT_NOSUB, OPT_TYPE_BOOLEAN},
151 #define OPT_SEC 9
152 {SHOPT_SEC, OPT_SEC, OPT_TYPE_SECURITY},
153 #define OPT_PUBLIC 10
154 {SHOPT_PUBLIC, OPT_PUBLIC, OPT_TYPE_BOOLEAN, OPT_SHARE_ONLY},
155 #define OPT_INDEX 11
156 {SHOPT_INDEX, OPT_INDEX, OPT_TYPE_FILE},
157 #define OPT_LOG 12
158 {SHOPT_LOG, OPT_LOG, OPT_TYPE_LOGTAG},
159 #define OPT_CKSUM 13
160 {SHOPT_CKSUM, OPT_CKSUM, OPT_TYPE_STRINGSET},
161 #define OPT_NONE 14
162 {SHOPT_NONE, OPT_NONE, OPT_TYPE_ACCLIST},
163 #define OPT_ROOT_MAPPING 15
164 {SHOPT_ROOT_MAPPING, OPT_ROOT_MAPPING, OPT_TYPE_USER},
165 #define OPT_CHARSET_MAP 16
166 {"", OPT_CHARSET_MAP, OPT_TYPE_ACCLIST},
167 #define OPT_NOACLFAB 17
168 {SHOPT_NOACLFAB, OPT_NOACLFAB, OPT_TYPE_BOOLEAN},
169 #define OPT_NOHIDE 18
170 {SHOPT_NOHIDE, OPT_NOHIDE, OPT_TYPE_BOOLEAN},
171
172 #ifdef VOLATILE_FH_TEST /* XXX added for testing volatile fh's only */
173 #define OPT_VOLFH 19
174 {SHOPT_VOLFH, OPT_VOLFH},
175 #endif /* VOLATILE_FH_TEST */
176 NULL
177 };
178
179 /*
180 * Codesets that may need to be converted to UTF-8 for file paths.
181 * Add new names here to add new property support. If we ever get a
182 * way to query the kernel for character sets, this should become
183 * dynamically loaded. Make sure changes here are reflected in
184 * cmd/fs.d/nfs/mountd/nfscmd.c
185 */
186
187 static char *legal_conv[] = {
188 "euc-cn",
189 "euc-jp",
190 "euc-jpms",
191 "euc-kr",
192 "euc-tw",
193 "iso8859-1",
194 "iso8859-2",
195 "iso8859-5",
196 "iso8859-6",
197 "iso8859-7",
198 "iso8859-8",
199 "iso8859-9",
200 "iso8859-13",
201 "iso8859-15",
202 "koi8-r",
203 NULL
204 };
205
206 /*
207 * list of properties that are related to security flavors.
208 */
209 static char *seclist[] = {
210 SHOPT_RO,
211 SHOPT_RW,
212 SHOPT_ROOT,
213 SHOPT_WINDOW,
214 SHOPT_NONE,
215 SHOPT_ROOT_MAPPING,
216 NULL
217 };
218
219 /* structure for list of securities */
220 struct securities {
221 sa_security_t security;
222 struct securities *next;
223 };
224
225 /*
226 * findcharset(charset)
227 *
228 * Returns B_TRUE if the charset is a legal conversion otherwise
229 * B_FALSE. This will need to be rewritten to be more efficient when
230 * we have a dynamic list of legal conversions.
231 */
232
233 static boolean_t
234 findcharset(char *charset)
235 {
236 int i;
237
238 for (i = 0; legal_conv[i] != NULL; i++)
239 if (strcmp(charset, legal_conv[i]) == 0)
240 return (B_TRUE);
241 return (B_FALSE);
242 }
243
244 /*
245 * findopt(name)
246 *
247 * Lookup option "name" in the option table and return the table
248 * index.
249 */
250
251 static int
252 findopt(char *name)
253 {
254 int i;
255 if (name != NULL) {
256 for (i = 0; optdefs[i].tag != NULL; i++) {
257 if (strcmp(optdefs[i].tag, name) == 0)
258 return (i);
259 }
260 if (findcharset(name))
261 return (OPT_CHARSET_MAP);
262 }
263 return (-1);
264 }
265
266 /*
267 * gettype(name)
268 *
269 * Return the type of option "name".
270 */
271
272 static int
273 gettype(char *name)
274 {
275 int optdef;
276
277 optdef = findopt(name);
278 if (optdef != -1)
279 return (optdefs[optdef].type);
280 return (OPT_TYPE_ANY);
281 }
282
283 /*
284 * nfs_validate_security_mode(mode)
285 *
286 * is the specified mode string a valid one for use with NFS?
287 */
288
289 static int
290 nfs_validate_security_mode(char *mode)
291 {
292 seconfig_t secinfo;
293 int err;
294
295 (void) memset(&secinfo, '\0', sizeof (secinfo));
296 err = nfs_getseconfig_byname(mode, &secinfo);
297 if (err == SC_NOERROR)
298 return (1);
299 return (0);
300 }
301
302 /*
303 * nfs_is_security_opt(tok)
304 *
305 * check to see if tok represents an option that is only valid in some
306 * security flavor.
307 */
308
309 static int
310 nfs_is_security_opt(char *tok)
311 {
312 int i;
313
314 for (i = 0; seclist[i] != NULL; i++) {
315 if (strcmp(tok, seclist[i]) == 0)
316 return (1);
317 }
318 return (0);
319 }
320
321 /*
322 * find_security(seclist, sec)
323 *
324 * Walk the current list of security flavors and return true if it is
325 * present, else return false.
326 */
327
328 static int
329 find_security(struct securities *seclist, sa_security_t sec)
330 {
331 while (seclist != NULL) {
332 if (seclist->security == sec)
333 return (1);
334 seclist = seclist->next;
335 }
336 return (0);
337 }
338
339 /*
340 * make_security_list(group, securitymodes, proto)
341 * go through the list of securitymodes and add them to the
342 * group's list of security optionsets. We also keep a list of
343 * those optionsets so we don't have to find them later. All of
344 * these will get copies of the same properties.
345 */
346
347 static struct securities *
348 make_security_list(sa_group_t group, char *securitymodes, char *proto)
349 {
350 char *tok, *next = NULL;
351 struct securities *curp, *headp = NULL, *prev;
352 sa_security_t check;
353 int freetok = 0;
354
355 for (tok = securitymodes; tok != NULL; tok = next) {
356 next = strchr(tok, ':');
357 if (next != NULL)
358 *next++ = '\0';
359 if (strcmp(tok, "default") == 0) {
360 /* resolve default into the real type */
361 tok = nfs_space_alias(tok);
362 freetok = 1;
363 }
364 check = sa_get_security(group, tok, proto);
365
366 /* add to the security list if it isn't there already */
367 if (check == NULL || !find_security(headp, check)) {
368 curp = (struct securities *)calloc(1,
369 sizeof (struct securities));
370 if (curp != NULL) {
371 if (check == NULL) {
372 curp->security = sa_create_security(
373 group, tok, proto);
374 } else {
375 curp->security = check;
376 }
377 /*
378 * note that the first time through the loop,
379 * headp will be NULL and prev will be
380 * undefined. Since headp is NULL, we set
381 * both it and prev to the curp (first
382 * structure to be allocated).
383 *
384 * later passes through the loop will have
385 * headp not being NULL and prev will be used
386 * to allocate at the end of the list.
387 */
388 if (headp == NULL) {
389 headp = curp;
390 prev = curp;
391 } else {
392 prev->next = curp;
393 prev = curp;
394 }
395 }
396 }
397
398 if (freetok) {
399 freetok = 0;
400 sa_free_attr_string(tok);
401 }
402 }
403 return (headp);
404 }
405
406 static void
407 free_security_list(struct securities *sec)
408 {
409 struct securities *next;
410 if (sec != NULL) {
411 for (next = sec->next; sec != NULL; sec = next) {
412 next = sec->next;
413 free(sec);
414 }
415 }
416 }
417
418 /*
419 * nfs_alistcat(str1, str2, sep)
420 *
421 * concatenate str1 and str2 into a new string using sep as a separate
422 * character. If memory allocation fails, return NULL;
423 */
424
425 static char *
426 nfs_alistcat(char *str1, char *str2, char sep)
427 {
428 char *newstr;
429 size_t len;
430
431 len = strlen(str1) + strlen(str2) + 2;
432 newstr = (char *)malloc(len);
433 if (newstr != NULL)
434 (void) snprintf(newstr, len, "%s%c%s", str1, sep, str2);
435 return (newstr);
436 }
437
438 /*
439 * add_security_prop(sec, name, value, persist)
440 *
441 * Add the property to the securities structure. This accumulates
442 * properties for as part of parsing legacy options.
443 */
444
445 static int
446 add_security_prop(struct securities *sec, char *name, char *value,
447 int persist, int iszfs)
448 {
449 sa_property_t prop;
450 int ret = SA_OK;
451
452 for (; sec != NULL; sec = sec->next) {
453 if (value == NULL) {
454 if (strcmp(name, SHOPT_RW) == 0 ||
455 strcmp(name, SHOPT_RO) == 0)
456 value = "*";
457 else
458 value = "true";
459 }
460
461 /*
462 * Get the existing property, if it exists, so we can
463 * determine what to do with it. The ro/rw/root
464 * properties can be merged if multiple instances of
465 * these properies are given. For example, if "rw"
466 * exists with a value "host1" and a later token of
467 * rw="host2" is seen, the values are merged into a
468 * single rw="host1:host2".
469 */
470 prop = sa_get_property(sec->security, name);
471
472 if (prop != NULL) {
473 char *oldvalue;
474 char *newvalue;
475
476 /*
477 * The security options of ro/rw/root might appear
478 * multiple times. If they do, the values need to be
479 * merged into an access list. If it was previously
480 * empty, the new value alone is added.
481 */
482 oldvalue = sa_get_property_attr(prop, "value");
483 if (oldvalue != NULL) {
484 /*
485 * The general case is to concatenate the new
486 * value onto the old value for multiple
487 * rw(ro/root) properties. A special case
488 * exists when either the old or new is the
489 * "all" case. In the special case, if both
490 * are "all", then it is "all", else if one is
491 * an access-list, that replaces the "all".
492 */
493 if (strcmp(oldvalue, "*") == 0) {
494 /* Replace old value with new value. */
495 newvalue = strdup(value);
496 } else if (strcmp(value, "*") == 0 ||
497 strcmp(oldvalue, value) == 0) {
498 /*
499 * Keep old value and ignore
500 * the new value.
501 */
502 newvalue = NULL;
503 } else {
504 /*
505 * Make a new list of old plus new
506 * access-list.
507 */
508 newvalue = nfs_alistcat(oldvalue,
509 value, ':');
510 }
511
512 if (newvalue != NULL) {
513 (void) sa_remove_property(prop);
514 prop = sa_create_property(name,
515 newvalue);
516 ret = sa_add_property(sec->security,
517 prop);
518 free(newvalue);
519 }
520 if (oldvalue != NULL)
521 sa_free_attr_string(oldvalue);
522 }
523 } else {
524 prop = sa_create_property(name, value);
525 ret = sa_add_property(sec->security, prop);
526 }
527 if (ret == SA_OK && !iszfs) {
528 ret = sa_commit_properties(sec->security, !persist);
529 }
530 }
531 return (ret);
532 }
533
534 /*
535 * check to see if group/share is persistent.
536 */
537 static int
538 is_persistent(sa_group_t group)
539 {
540 char *type;
541 int persist = 1;
542
543 type = sa_get_group_attr(group, "type");
544 if (type != NULL && strcmp(type, "persist") != 0)
545 persist = 0;
546 if (type != NULL)
547 sa_free_attr_string(type);
548 return (persist);
549 }
550
551 /*
552 * invalid_security(options)
553 *
554 * search option string for any invalid sec= type.
555 * return true (1) if any are not valid else false (0)
556 */
557 static int
558 invalid_security(char *options)
559 {
560 char *copy, *base, *token, *value;
561 int ret = 0;
562
563 copy = strdup(options);
564 token = base = copy;
565 while (token != NULL && ret == 0) {
566 token = strtok(base, ",");
567 base = NULL;
568 if (token != NULL) {
569 value = strchr(token, '=');
570 if (value != NULL)
571 *value++ = '\0';
572 if (strcmp(token, "sec") == 0) {
573 /* HAVE security flavors so check them */
574 char *tok, *next;
575 for (next = NULL, tok = value; tok != NULL;
576 tok = next) {
577 next = strchr(tok, ':');
578 if (next != NULL)
579 *next++ = '\0';
580 ret = !nfs_validate_security_mode(tok);
581 if (ret)
582 break;
583 }
584 }
585 }
586 }
587 if (copy != NULL)
588 free(copy);
589 return (ret);
590 }
591
592 /*
593 * nfs_parse_legacy_options(group, options)
594 *
595 * Parse the old style options into internal format and store on the
596 * specified group. Group could be a share for full legacy support.
597 */
598
599 static int
600 nfs_parse_legacy_options(sa_group_t group, char *options)
601 {
602 char *dup;
603 char *base;
604 char *token;
605 sa_optionset_t optionset;
606 struct securities *security_list = NULL;
607 sa_property_t prop;
608 int ret = SA_OK;
609 int iszfs = 0;
610 sa_group_t parent;
611 int persist = 0;
612 char *lasts;
613
614 /* do we have an existing optionset? */
615 optionset = sa_get_optionset(group, "nfs");
616 if (optionset == NULL) {
617 /* didn't find existing optionset so create one */
618 optionset = sa_create_optionset(group, "nfs");
619 } else {
620 /*
621 * Have an existing optionset . Ideally, we would need
622 * to compare options in order to detect errors. For
623 * now, we assume that the first optionset is the
624 * correct one and the others will be the same. An
625 * empty optionset is the same as no optionset so we
626 * don't want to exit in that case. Getting an empty
627 * optionset can occur with ZFS property checking.
628 */
629 if (sa_get_property(optionset, NULL) != NULL)
630 return (ret);
631 }
632
633 if (strcmp(options, SHOPT_RW) == 0) {
634 /*
635 * there is a special case of only the option "rw"
636 * being the default option. We don't have to do
637 * anything.
638 */
639 return (ret);
640 }
641
642 /*
643 * check if security types are present and validate them. If
644 * any are not legal, fail.
645 */
646
647 if (invalid_security(options)) {
648 return (SA_INVALID_SECURITY);
649 }
650
651 /*
652 * in order to not attempt to change ZFS properties unless
653 * absolutely necessary, we never do it in the legacy parsing.
654 */
655 if (sa_is_share(group)) {
656 char *zfs;
657 parent = sa_get_parent_group(group);
658 if (parent != NULL) {
659 zfs = sa_get_group_attr(parent, "zfs");
660 if (zfs != NULL) {
661 sa_free_attr_string(zfs);
662 iszfs++;
663 }
664 }
665 } else {
666 iszfs = sa_group_is_zfs(group);
667 }
668
669 /* We need a copy of options for the next part. */
670 dup = strdup(options);
671 if (dup == NULL)
672 return (SA_NO_MEMORY);
673
674 /*
675 * we need to step through each option in the string and then
676 * add either the option or the security option as needed. If
677 * this is not a persistent share, don't commit to the
678 * repository. If there is an error, we also want to abort the
679 * processing and report it.
680 */
681 persist = is_persistent(group);
682 base = dup;
683 token = dup;
684 lasts = NULL;
685 while (token != NULL && ret == SA_OK) {
686 ret = SA_OK;
687 token = strtok_r(base, ",", &lasts);
688 base = NULL;
689 if (token != NULL) {
690 char *value;
691 /*
692 * if the option has a value, it will have an '=' to
693 * separate the name from the value. The following
694 * code will result in value != NULL and token
695 * pointing to just the name if there is a value.
696 */
697 value = strchr(token, '=');
698 if (value != NULL) {
699 *value++ = '\0';
700 }
701 if (strcmp(token, "sec") == 0 ||
702 strcmp(token, "secure") == 0) {
703 /*
704 * Once in security parsing, we only
705 * do security. We do need to move
706 * between the security node and the
707 * toplevel. The security tag goes on
708 * the root while the following ones
709 * go on the security.
710 */
711 if (security_list != NULL) {
712 /*
713 * have an old list so close it and
714 * start the new
715 */
716 free_security_list(security_list);
717 }
718 if (strcmp(token, "secure") == 0) {
719 value = "dh";
720 } else {
721 if (value == NULL) {
722 ret = SA_SYNTAX_ERR;
723 break;
724 }
725 }
726 security_list = make_security_list(group,
727 value, "nfs");
728 } else {
729 /*
730 * Note that the "old" syntax allowed a
731 * default security model This must be
732 * accounted for and internally converted to
733 * "standard" security structure.
734 */
735 if (nfs_is_security_opt(token)) {
736 if (security_list == NULL) {
737 /*
738 * need to have a
739 * security
740 * option. This will
741 * be "closed" when a
742 * defined "sec="
743 * option is
744 * seen. This is
745 * technically an
746 * error but will be
747 * allowed with
748 * warning.
749 */
750 security_list =
751 make_security_list(group,
752 "default",
753 "nfs");
754 }
755 if (security_list != NULL) {
756 ret = add_security_prop(
757 security_list, token,
758 value, persist, iszfs);
759 } else {
760 ret = SA_NO_MEMORY;
761 }
762 } else {
763 /* regular options */
764 if (value == NULL) {
765 if (strcmp(token, SHOPT_RW) ==
766 0 || strcmp(token,
767 SHOPT_RO) == 0) {
768 value = "*";
769 } else {
770 value = "global";
771 if (strcmp(token,
772 SHOPT_LOG) != 0) {
773 value = "true";
774 }
775 }
776 }
777 /*
778 * In all cases, create the
779 * property specified. If the
780 * value was NULL, the default
781 * value will have been
782 * substituted.
783 */
784 prop = sa_create_property(token, value);
785 ret = sa_add_property(optionset, prop);
786 if (ret != SA_OK)
787 break;
788
789 if (!iszfs) {
790 ret = sa_commit_properties(
791 optionset, !persist);
792 }
793 }
794 }
795 }
796 }
797 if (security_list != NULL)
798 free_security_list(security_list);
799
800 free(dup);
801 return (ret);
802 }
803
804 /*
805 * is_a_number(number)
806 *
807 * is the string a number in one of the forms we want to use?
808 */
809
810 static int
811 is_a_number(char *number)
812 {
813 int ret = 1;
814 int hex = 0;
815
816 if (strncmp(number, "0x", 2) == 0) {
817 number += 2;
818 hex = 1;
819 } else if (*number == '-') {
820 number++; /* skip the minus */
821 }
822 while (ret == 1 && *number != '\0') {
823 if (hex) {
824 ret = isxdigit(*number++);
825 } else {
826 ret = isdigit(*number++);
827 }
828 }
829 return (ret);
830 }
831
832 /*
833 * Look for the specified tag in the configuration file. If it is found,
834 * enable logging and set the logging configuration information for exp.
835 */
836 static void
837 configlog(struct exportdata *exp, char *tag)
838 {
839 nfsl_config_t *configlist = NULL, *configp;
840 int error = 0;
841 char globaltag[] = DEFAULTTAG;
842
843 /*
844 * Sends config errors to stderr
845 */
846 nfsl_errs_to_syslog = B_FALSE;
847
848 /*
849 * get the list of configuration settings
850 */
851 error = nfsl_getconfig_list(&configlist);
852 if (error) {
853 (void) fprintf(stderr,
854 dgettext(TEXT_DOMAIN, "Cannot get log configuration: %s\n"),
855 strerror(error));
856 }
857
858 if (tag == NULL)
859 tag = globaltag;
860 if ((configp = nfsl_findconfig(configlist, tag, &error)) == NULL) {
861 nfsl_freeconfig_list(&configlist);
862 (void) fprintf(stderr,
863 dgettext(TEXT_DOMAIN, "No tags matching \"%s\"\n"), tag);
864 /* bad configuration */
865 error = ENOENT;
866 goto err;
867 }
868
869 if ((exp->ex_tag = strdup(tag)) == NULL) {
870 error = ENOMEM;
871 goto out;
872 }
873 if ((exp->ex_log_buffer = strdup(configp->nc_bufferpath)) == NULL) {
874 error = ENOMEM;
875 goto out;
876 }
877 exp->ex_flags |= EX_LOG;
878 if (configp->nc_rpclogpath != NULL)
879 exp->ex_flags |= EX_LOG_ALLOPS;
880 out:
881 if (configlist != NULL)
882 nfsl_freeconfig_list(&configlist);
883
884 err:
885 if (error != 0) {
886 if (exp->ex_flags != NULL)
887 free(exp->ex_tag);
888 if (exp->ex_log_buffer != NULL)
889 free(exp->ex_log_buffer);
890 (void) fprintf(stderr,
891 dgettext(TEXT_DOMAIN, "Cannot set log configuration: %s\n"),
892 strerror(error));
893 }
894 }
895
896 /*
897 * fill_export_from_optionset(export, optionset)
898 *
899 * In order to share, we need to set all the possible general options
900 * into the export structure. Share info will be filled in by the
901 * caller. Various property values get turned into structure specific
902 * values.
903 */
904
905 static int
906 fill_export_from_optionset(struct exportdata *export, sa_optionset_t optionset)
907 {
908 sa_property_t option;
909 int ret = SA_OK;
910
911 for (option = sa_get_property(optionset, NULL);
912 option != NULL; option = sa_get_next_property(option)) {
913 char *name;
914 char *value;
915 uint32_t val;
916
917 /*
918 * since options may be set/reset multiple times, always do an
919 * explicit set or clear of the option. This allows defaults
920 * to be set and then the protocol specific to override.
921 */
922
923 name = sa_get_property_attr(option, "type");
924 value = sa_get_property_attr(option, "value");
925 switch (findopt(name)) {
926 case OPT_ANON:
927 if (value != NULL && is_a_number(value)) {
928 val = strtoul(value, NULL, 0);
929 } else {
930 struct passwd *pw;
931 pw = getpwnam(value != NULL ? value : "nobody");
932 if (pw != NULL) {
933 val = pw->pw_uid;
934 } else {
935 val = UID_NOBODY;
936 }
937 endpwent();
938 }
939 export->ex_anon = val;
940 break;
941 case OPT_NOSUID:
942 if (value != NULL && (strcasecmp(value, "true") == 0 ||
943 strcmp(value, "1") == 0))
944 export->ex_flags |= EX_NOSUID;
945 else
946 export->ex_flags &= ~EX_NOSUID;
947 break;
948 case OPT_ACLOK:
949 if (value != NULL && (strcasecmp(value, "true") == 0 ||
950 strcmp(value, "1") == 0))
951 export->ex_flags |= EX_ACLOK;
952 else
953 export->ex_flags &= ~EX_ACLOK;
954 break;
955 case OPT_NOSUB:
956 if (value != NULL && (strcasecmp(value, "true") == 0 ||
957 strcmp(value, "1") == 0))
958 export->ex_flags |= EX_NOSUB;
959 else
960 export->ex_flags &= ~EX_NOSUB;
961 break;
962 case OPT_PUBLIC:
963 if (value != NULL && (strcasecmp(value, "true") == 0 ||
964 strcmp(value, "1") == 0))
965 export->ex_flags |= EX_PUBLIC;
966 else
967 export->ex_flags &= ~EX_PUBLIC;
968 break;
969 case OPT_INDEX:
970 if (value != NULL && (strcmp(value, "..") == 0 ||
971 strchr(value, '/') != NULL)) {
972 /* this is an error */
973 (void) printf(dgettext(TEXT_DOMAIN,
974 "NFS: index=\"%s\" not valid;"
975 "must be a filename.\n"),
976 value);
977 break;
978 }
979 if (value != NULL && *value != '\0' &&
980 strcmp(value, ".") != 0) {
981 /* valid index file string */
982 if (export->ex_index != NULL) {
983 /* left over from "default" */
984 free(export->ex_index);
985 }
986 /* remember to free */
987 export->ex_index = strdup(value);
988 if (export->ex_index == NULL) {
989 (void) printf(dgettext(TEXT_DOMAIN,
990 "NFS: out of memory setting "
991 "index property\n"));
992 break;
993 }
994 export->ex_flags |= EX_INDEX;
995 }
996 break;
997 case OPT_LOG:
998 if (value == NULL)
999 value = strdup("global");
1000 if (value != NULL)
1001 configlog(export,
1002 strlen(value) ? value : "global");
1003 break;
1004 case OPT_CHARSET_MAP:
1005 /*
1006 * Set EX_CHARMAP when there is at least one
1007 * charmap conversion property. This will get
1008 * checked by the nfs server when it needs to.
1009 */
1010 export->ex_flags |= EX_CHARMAP;
1011 break;
1012 case OPT_NOACLFAB:
1013 if (value != NULL && (strcasecmp(value, "true") == 0 ||
1014 strcmp(value, "1") == 0))
1015 export->ex_flags |= EX_NOACLFAB;
1016 else
1017 export->ex_flags &= ~EX_NOACLFAB;
1018 break;
1019 case OPT_NOHIDE:
1020 if (value != NULL && (strcasecmp(value, "true") == 0 ||
1021 strcmp(value, "1") == 0))
1022 export->ex_flags |= EX_NOHIDE;
1023 else
1024 export->ex_flags &= ~EX_NOHIDE;
1025
1026 break;
1027 default:
1028 /* have a syntactic error */
1029 (void) printf(dgettext(TEXT_DOMAIN,
1030 "NFS: unrecognized option %s=%s\n"),
1031 name != NULL ? name : "",
1032 value != NULL ? value : "");
1033 break;
1034 }
1035 if (name != NULL)
1036 sa_free_attr_string(name);
1037 if (value != NULL)
1038 sa_free_attr_string(value);
1039 }
1040 return (ret);
1041 }
1042
1043 /*
1044 * cleanup_export(export)
1045 *
1046 * Cleanup the allocated areas so we don't leak memory
1047 */
1048
1049 static void
1050 cleanup_export(struct exportdata *export)
1051 {
1052 int i;
1053
1054 if (export->ex_index != NULL)
1055 free(export->ex_index);
1056 if (export->ex_secinfo != NULL) {
1057 for (i = 0; i < export->ex_seccnt; i++)
1058 if (export->ex_secinfo[i].s_rootnames != NULL)
1059 free(export->ex_secinfo[i].s_rootnames);
1060 free(export->ex_secinfo);
1061 }
1062 }
1063
1064 /*
1065 * Given a seconfig entry and a colon-separated
1066 * list of names, allocate an array big enough
1067 * to hold the root list, then convert each name to
1068 * a principal name according to the security
1069 * info and assign it to an array element.
1070 * Return the array and its size.
1071 */
1072 static caddr_t *
1073 get_rootnames(seconfig_t *sec, char *list, int *count)
1074 {
1075 caddr_t *a;
1076 int c, i;
1077 char *host, *p;
1078
1079 /*
1080 * Count the number of strings in the list.
1081 * This is the number of colon separators + 1.
1082 */
1083 c = 1;
1084 for (p = list; *p; p++)
1085 if (*p == ':')
1086 c++;
1087 *count = c;
1088
1089 a = (caddr_t *)malloc(c * sizeof (char *));
1090 if (a == NULL) {
1091 (void) printf(dgettext(TEXT_DOMAIN,
1092 "get_rootnames: no memory\n"));
1093 } else {
1094 for (i = 0; i < c; i++) {
1095 host = strtok(list, ":");
1096 if (!nfs_get_root_principal(sec, host, &a[i])) {
1097 free(a);
1098 a = NULL;
1099 break;
1100 }
1101 list = NULL;
1102 }
1103 }
1104
1105 return (a);
1106 }
1107
1108 /*
1109 * fill_security_from_secopts(sp, secopts)
1110 *
1111 * Fill the secinfo structure from the secopts optionset.
1112 */
1113
1114 static int
1115 fill_security_from_secopts(struct secinfo *sp, sa_security_t secopts)
1116 {
1117 sa_property_t prop;
1118 char *type;
1119 int longform;
1120 int err = SC_NOERROR;
1121 uint32_t val;
1122
1123 type = sa_get_security_attr(secopts, "sectype");
1124 if (type != NULL) {
1125 /* named security type needs secinfo to be filled in */
1126 err = nfs_getseconfig_byname(type, &sp->s_secinfo);
1127 sa_free_attr_string(type);
1128 if (err != SC_NOERROR)
1129 return (err);
1130 } else {
1131 /* default case */
1132 err = nfs_getseconfig_default(&sp->s_secinfo);
1133 if (err != SC_NOERROR)
1134 return (err);
1135 }
1136
1137 err = SA_OK;
1138 for (prop = sa_get_property(secopts, NULL);
1139 prop != NULL && err == SA_OK;
1140 prop = sa_get_next_property(prop)) {
1141 char *name;
1142 char *value;
1143
1144 name = sa_get_property_attr(prop, "type");
1145 value = sa_get_property_attr(prop, "value");
1146
1147 longform = value != NULL && strcmp(value, "*") != 0;
1148
1149 switch (findopt(name)) {
1150 case OPT_RO:
1151 sp->s_flags |= longform ? M_ROL : M_RO;
1152 break;
1153 case OPT_RW:
1154 sp->s_flags |= longform ? M_RWL : M_RW;
1155 break;
1156 case OPT_ROOT:
1157 sp->s_flags |= M_ROOT;
1158 /*
1159 * if we are using AUTH_UNIX, handle like other things
1160 * such as RO/RW
1161 */
1162 if (sp->s_secinfo.sc_rpcnum == AUTH_UNIX)
1163 continue;
1164 /* not AUTH_UNIX */
1165 if (value != NULL) {
1166 sp->s_rootnames = get_rootnames(&sp->s_secinfo,
1167 value, &sp->s_rootcnt);
1168 if (sp->s_rootnames == NULL) {
1169 err = SA_BAD_VALUE;
1170 (void) fprintf(stderr,
1171 dgettext(TEXT_DOMAIN,
1172 "Bad root list\n"));
1173 }
1174 }
1175 break;
1176 case OPT_NONE:
1177 sp->s_flags |= M_NONE;
1178 break;
1179 case OPT_WINDOW:
1180 if (value != NULL) {
1181 sp->s_window = atoi(value);
1182 /* just in case */
1183 if (sp->s_window < 0)
1184 sp->s_window = DEF_WIN;
1185 }
1186 break;
1187 case OPT_ROOT_MAPPING:
1188 if (value != NULL && is_a_number(value)) {
1189 val = strtoul(value, NULL, 0);
1190 } else {
1191 struct passwd *pw;
1192 pw = getpwnam(value != NULL ? value : "nobody");
1193 if (pw != NULL) {
1194 val = pw->pw_uid;
1195 } else {
1196 val = UID_NOBODY;
1197 }
1198 endpwent();
1199 }
1200 sp->s_rootid = val;
1201 break;
1202 default:
1203 break;
1204 }
1205 if (name != NULL)
1206 sa_free_attr_string(name);
1207 if (value != NULL)
1208 sa_free_attr_string(value);
1209 }
1210 /* if rw/ro options not set, use default of RW */
1211 if ((sp->s_flags & NFS_RWMODES) == 0)
1212 sp->s_flags |= M_RW;
1213 return (err);
1214 }
1215
1216 /*
1217 * This is for testing only
1218 * It displays the export structure that
1219 * goes into the kernel.
1220 */
1221 static void
1222 printarg(char *path, struct exportdata *ep)
1223 {
1224 int i, j;
1225 struct secinfo *sp;
1226
1227 if (debug == 0)
1228 return;
1229
1230 (void) printf("%s:\n", path);
1231 (void) printf("\tex_version = %d\n", ep->ex_version);
1232 (void) printf("\tex_path = %s\n", ep->ex_path);
1233 (void) printf("\tex_pathlen = %ld\n", (ulong_t)ep->ex_pathlen);
1234 (void) printf("\tex_flags: (0x%02x) ", ep->ex_flags);
1235 if (ep->ex_flags & EX_NOSUID)
1236 (void) printf("NOSUID ");
1237 if (ep->ex_flags & EX_ACLOK)
1238 (void) printf("ACLOK ");
1239 if (ep->ex_flags & EX_PUBLIC)
1240 (void) printf("PUBLIC ");
1241 if (ep->ex_flags & EX_NOSUB)
1242 (void) printf("NOSUB ");
1243 if (ep->ex_flags & EX_LOG)
1244 (void) printf("LOG ");
1245 if (ep->ex_flags & EX_CHARMAP)
1246 (void) printf("CHARMAP ");
1247 if (ep->ex_flags & EX_LOG_ALLOPS)
1248 (void) printf("LOG_ALLOPS ");
1249 if (ep->ex_flags == 0)
1250 (void) printf("(none)");
1251 (void) printf("\n");
1252 if (ep->ex_flags & EX_LOG) {
1253 (void) printf("\tex_log_buffer = %s\n",
1254 (ep->ex_log_buffer ? ep->ex_log_buffer : "(NULL)"));
1255 (void) printf("\tex_tag = %s\n",
1256 (ep->ex_tag ? ep->ex_tag : "(NULL)"));
1257 }
1258 (void) printf("\tex_anon = %d\n", ep->ex_anon);
1259 (void) printf("\tex_seccnt = %d\n", ep->ex_seccnt);
1260 (void) printf("\n");
1261 for (i = 0; i < ep->ex_seccnt; i++) {
1262 sp = &ep->ex_secinfo[i];
1263 (void) printf("\t\ts_secinfo = %s\n", sp->s_secinfo.sc_name);
1264 (void) printf("\t\ts_flags: (0x%02x) ", sp->s_flags);
1265 if (sp->s_flags & M_ROOT) (void) printf("M_ROOT ");
1266 if (sp->s_flags & M_RO) (void) printf("M_RO ");
1267 if (sp->s_flags & M_ROL) (void) printf("M_ROL ");
1268 if (sp->s_flags & M_RW) (void) printf("M_RW ");
1269 if (sp->s_flags & M_RWL) (void) printf("M_RWL ");
1270 if (sp->s_flags & M_NONE) (void) printf("M_NONE ");
1271 if (sp->s_flags == 0) (void) printf("(none)");
1272 (void) printf("\n");
1273 (void) printf("\t\ts_window = %d\n", sp->s_window);
1274 (void) printf("\t\ts_rootid = %d\n", sp->s_rootid);
1275 (void) printf("\t\ts_rootcnt = %d ", sp->s_rootcnt);
1276 (void) fflush(stdout);
1277 for (j = 0; j < sp->s_rootcnt; j++)
1278 (void) printf("%s ", sp->s_rootnames[j] ?
1279 sp->s_rootnames[j] : "<null>");
1280 (void) printf("\n\n");
1281 }
1282 }
1283
1284 /*
1285 * count_security(opts)
1286 *
1287 * Count the number of security types (flavors). The optionset has
1288 * been populated with the security flavors as a holding mechanism.
1289 * We later use this number to allocate data structures.
1290 */
1291
1292 static int
1293 count_security(sa_optionset_t opts)
1294 {
1295 int count = 0;
1296 sa_property_t prop;
1297 if (opts != NULL) {
1298 for (prop = sa_get_property(opts, NULL); prop != NULL;
1299 prop = sa_get_next_property(prop)) {
1300 count++;
1301 }
1302 }
1303 return (count);
1304 }
1305
1306 /*
1307 * nfs_sprint_option(rbuff, rbuffsize, incr, prop, sep)
1308 *
1309 * provides a mechanism to format NFS properties into legacy output
1310 * format. If the buffer would overflow, it is reallocated and grown
1311 * as appropriate. Special cases of converting internal form of values
1312 * to those used by "share" are done. this function does one property
1313 * at a time.
1314 */
1315
1316 static int
1317 nfs_sprint_option(char **rbuff, size_t *rbuffsize, size_t incr,
1318 sa_property_t prop, int sep)
1319 {
1320 char *name;
1321 char *value;
1322 int curlen;
1323 char *buff = *rbuff;
1324 size_t buffsize = *rbuffsize;
1325 int printed = B_FALSE;
1326
1327 name = sa_get_property_attr(prop, "type");
1328 value = sa_get_property_attr(prop, "value");
1329 if (buff != NULL)
1330 curlen = strlen(buff);
1331 else
1332 curlen = 0;
1333 if (name != NULL) {
1334 int len;
1335 len = strlen(name) + sep;
1336
1337 /*
1338 * A future RFE would be to replace this with more
1339 * generic code and to possibly handle more types.
1340 */
1341 switch (gettype(name)) {
1342 case OPT_TYPE_BOOLEAN:
1343 /*
1344 * For NFS, boolean value of FALSE means it
1345 * doesn't show up in the option list at all.
1346 */
1347 if (value != NULL && strcasecmp(value, "false") == 0)
1348 goto skip;
1349 if (value != NULL) {
1350 sa_free_attr_string(value);
1351 value = NULL;
1352 }
1353 break;
1354 case OPT_TYPE_ACCLIST:
1355 if (value != NULL && strcmp(value, "*") == 0) {
1356 sa_free_attr_string(value);
1357 value = NULL;
1358 } else {
1359 if (value != NULL)
1360 len += 1 + strlen(value);
1361 }
1362 break;
1363 case OPT_TYPE_LOGTAG:
1364 if (value != NULL && strlen(value) == 0) {
1365 sa_free_attr_string(value);
1366 value = NULL;
1367 } else {
1368 if (value != NULL)
1369 len += 1 + strlen(value);
1370 }
1371 break;
1372 default:
1373 if (value != NULL)
1374 len += 1 + strlen(value);
1375 break;
1376 }
1377 while (buffsize <= (curlen + len)) {
1378 /* need more room */
1379 buffsize += incr;
1380 buff = realloc(buff, buffsize);
1381 if (buff == NULL) {
1382 /* realloc failed so free everything */
1383 if (*rbuff != NULL)
1384 free(*rbuff);
1385 }
1386 *rbuff = buff;
1387 *rbuffsize = buffsize;
1388 if (buff == NULL)
1389 goto skip;
1390
1391 }
1392
1393 if (buff == NULL)
1394 goto skip;
1395
1396 if (value == NULL) {
1397 (void) snprintf(buff + curlen, buffsize - curlen,
1398 "%s%s", sep ? "," : "",
1399 name, value != NULL ? value : "");
1400 } else {
1401 (void) snprintf(buff + curlen, buffsize - curlen,
1402 "%s%s=%s", sep ? "," : "",
1403 name, value != NULL ? value : "");
1404 }
1405 printed = B_TRUE;
1406 }
1407 skip:
1408 if (name != NULL)
1409 sa_free_attr_string(name);
1410 if (value != NULL)
1411 sa_free_attr_string(value);
1412 return (printed);
1413 }
1414
1415 /*
1416 * nfs_format_options(group, hier)
1417 *
1418 * format all the options on the group into an old-style option
1419 * string. If hier is non-zero, walk up the tree to get inherited
1420 * options.
1421 */
1422
1423 static char *
1424 nfs_format_options(sa_group_t group, int hier)
1425 {
1426 sa_optionset_t options = NULL;
1427 sa_optionset_t secoptions = NULL;
1428 sa_property_t prop, secprop;
1429 sa_security_t security = NULL;
1430 char *buff;
1431 size_t buffsize;
1432 char *sectype = NULL;
1433 int sep = 0;
1434
1435
1436 buff = malloc(OPT_CHUNK);
1437 if (buff == NULL) {
1438 return (NULL);
1439 }
1440
1441 buff[0] = '\0';
1442 buffsize = OPT_CHUNK;
1443
1444 /*
1445 * We may have a an optionset relative to this item. format
1446 * these if we find them and then add any security definitions.
1447 */
1448
1449 options = sa_get_derived_optionset(group, "nfs", hier);
1450
1451 /*
1452 * do the default set first but skip any option that is also
1453 * in the protocol specific optionset.
1454 */
1455 if (options != NULL) {
1456 for (prop = sa_get_property(options, NULL);
1457 prop != NULL; prop = sa_get_next_property(prop)) {
1458 /*
1459 * use this one since we skipped any
1460 * of these that were also in
1461 * optdefault
1462 */
1463 if (nfs_sprint_option(&buff, &buffsize, OPT_CHUNK,
1464 prop, sep))
1465 sep = 1;
1466 if (buff == NULL) {
1467 /*
1468 * buff could become NULL if there
1469 * isn't enough memory for
1470 * nfs_sprint_option to realloc()
1471 * as necessary. We can't really
1472 * do anything about it at this
1473 * point so we return NULL. The
1474 * caller should handle the
1475 * failure.
1476 */
1477 if (options != NULL)
1478 sa_free_derived_optionset(
1479 options);
1480 return (buff);
1481 }
1482 }
1483 }
1484 secoptions = (sa_optionset_t)sa_get_all_security_types(group,
1485 "nfs", hier);
1486 if (secoptions != NULL) {
1487 for (secprop = sa_get_property(secoptions, NULL);
1488 secprop != NULL;
1489 secprop = sa_get_next_property(secprop)) {
1490 sectype = sa_get_property_attr(secprop, "type");
1491 security =
1492 (sa_security_t)sa_get_derived_security(
1493 group, sectype, "nfs", hier);
1494 if (security != NULL) {
1495 if (sectype != NULL) {
1496 prop = sa_create_property(
1497 "sec", sectype);
1498 if (prop == NULL)
1499 goto err;
1500 if (nfs_sprint_option(&buff,
1501 &buffsize, OPT_CHUNK, prop, sep))
1502 sep = 1;
1503 (void) sa_remove_property(prop);
1504 if (buff == NULL)
1505 goto err;
1506 }
1507 for (prop = sa_get_property(security,
1508 NULL); prop != NULL;
1509 prop = sa_get_next_property(prop)) {
1510 if (nfs_sprint_option(&buff,
1511 &buffsize, OPT_CHUNK, prop, sep))
1512 sep = 1;
1513 if (buff == NULL)
1514 goto err;
1515 }
1516 sa_free_derived_optionset(security);
1517 }
1518 if (sectype != NULL)
1519 sa_free_attr_string(sectype);
1520 }
1521 sa_free_derived_optionset(secoptions);
1522 }
1523
1524 if (options != NULL)
1525 sa_free_derived_optionset(options);
1526 return (buff);
1527
1528 err:
1529 /*
1530 * If we couldn't allocate memory for option printing, we need
1531 * to break out of the nested loops, cleanup and return NULL.
1532 */
1533 if (secoptions != NULL)
1534 sa_free_derived_optionset(secoptions);
1535 if (security != NULL)
1536 sa_free_derived_optionset(security);
1537 if (sectype != NULL)
1538 sa_free_attr_string(sectype);
1539 if (options != NULL)
1540 sa_free_derived_optionset(options);
1541 return (buff);
1542 }
1543
1544 /*
1545 * Append an entry to the nfslogtab file
1546 */
1547 static int
1548 nfslogtab_add(dir, buffer, tag)
1549 char *dir, *buffer, *tag;
1550 {
1551 FILE *f;
1552 struct logtab_ent lep;
1553 int error = 0;
1554
1555 /*
1556 * Open the file for update and create it if necessary.
1557 * This may leave the I/O offset at the end of the file,
1558 * so rewind back to the beginning of the file.
1559 */
1560 f = fopen(NFSLOGTAB, "a+");
1561 if (f == NULL) {
1562 error = errno;
1563 goto out;
1564 }
1565 rewind(f);
1566
1567 if (lockf(fileno(f), F_LOCK, 0L) < 0) {
1568 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1569 "share complete, however failed to lock %s "
1570 "for update: %s\n"), NFSLOGTAB, strerror(errno));
1571 error = -1;
1572 goto out;
1573 }
1574
1575 if (logtab_deactivate_after_boot(f) == -1) {
1576 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1577 "share complete, however could not deactivate "
1578 "entries in %s\n"), NFSLOGTAB);
1579 error = -1;
1580 goto out;
1581 }
1582
1583 /*
1584 * Remove entries matching buffer and sharepoint since we're
1585 * going to replace it with perhaps an entry with a new tag.
1586 */
1587 if (logtab_rement(f, buffer, dir, NULL, -1)) {
1588 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1589 "share complete, however could not remove matching "
1590 "entries in %s\n"), NFSLOGTAB);
1591 error = -1;
1592 goto out;
1593 }
1594
1595 /*
1596 * Deactivate all active entries matching this sharepoint
1597 */
1598 if (logtab_deactivate(f, NULL, dir, NULL)) {
1599 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1600 "share complete, however could not deactivate matching "
1601 "entries in %s\n"), NFSLOGTAB);
1602 error = -1;
1603 goto out;
1604 }
1605
1606 lep.le_buffer = buffer;
1607 lep.le_path = dir;
1608 lep.le_tag = tag;
1609 lep.le_state = LES_ACTIVE;
1610
1611 /*
1612 * Add new sharepoint / buffer location to nfslogtab
1613 */
1614 if (logtab_putent(f, &lep) < 0) {
1615 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1616 "share complete, however could not add %s to %s\n"),
1617 dir, NFSLOGTAB);
1618 error = -1;
1619 }
1620
1621 out:
1622 if (f != NULL)
1623 (void) fclose(f);
1624 return (error);
1625 }
1626
1627 /*
1628 * Deactivate an entry from the nfslogtab file
1629 */
1630 static int
1631 nfslogtab_deactivate(path)
1632 char *path;
1633 {
1634 FILE *f;
1635 int error = 0;
1636
1637 f = fopen(NFSLOGTAB, "r+");
1638 if (f == NULL) {
1639 error = errno;
1640 goto out;
1641 }
1642 if (lockf(fileno(f), F_LOCK, 0L) < 0) {
1643 error = errno;
1644 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1645 "share complete, however could not lock %s for "
1646 "update: %s\n"), NFSLOGTAB, strerror(error));
1647 goto out;
1648 }
1649 if (logtab_deactivate(f, NULL, path, NULL) == -1) {
1650 error = -1;
1651 (void) fprintf(stderr,
1652 dgettext(TEXT_DOMAIN,
1653 "share complete, however could not "
1654 "deactivate %s in %s\n"), path, NFSLOGTAB);
1655 goto out;
1656 }
1657
1658 out: if (f != NULL)
1659 (void) fclose(f);
1660
1661 return (error);
1662 }
1663
1664 /*
1665 * check_public(group, skipshare)
1666 *
1667 * Check the group for any shares that have the public property
1668 * enabled. We skip "skipshare" since that is the one we are
1669 * working with. This is a separate function to make handling
1670 * subgroups simpler. Returns true if there is a share with public.
1671 */
1672 static int
1673 check_public(sa_group_t group, sa_share_t skipshare)
1674 {
1675 int exists = B_FALSE;
1676 sa_share_t share;
1677 sa_optionset_t opt;
1678 sa_property_t prop;
1679 char *shared;
1680
1681 for (share = sa_get_share(group, NULL); share != NULL;
1682 share = sa_get_next_share(share)) {
1683 if (share == skipshare)
1684 continue;
1685
1686 opt = sa_get_optionset(share, "nfs");
1687 if (opt == NULL)
1688 continue;
1689 prop = sa_get_property(opt, "public");
1690 if (prop == NULL)
1691 continue;
1692 shared = sa_get_share_attr(share, "shared");
1693 if (shared != NULL) {
1694 exists = strcmp(shared, "true") == 0;
1695 sa_free_attr_string(shared);
1696 if (exists == B_TRUE)
1697 break;
1698 }
1699 }
1700
1701 return (exists);
1702 }
1703
1704 /*
1705 * public_exists(handle, share)
1706 *
1707 * check to see if public option is set on any other share than the
1708 * one specified. Need to check zfs sub-groups as well as the top
1709 * level groups.
1710 */
1711 static int
1712 public_exists(sa_handle_t handle, sa_share_t skipshare)
1713 {
1714 sa_group_t group = NULL;
1715
1716 /*
1717 * If we don't have a handle, we can only do syntax check. We
1718 * can't check against other shares so we assume OK and will
1719 * catch the problem only when we actually try to apply it.
1720 */
1721 if (handle == NULL)
1722 return (SA_OK);
1723
1724 if (skipshare != NULL) {
1725 group = sa_get_parent_group(skipshare);
1726 if (group == NULL)
1727 return (SA_NO_SUCH_GROUP);
1728 }
1729
1730 for (group = sa_get_group(handle, NULL); group != NULL;
1731 group = sa_get_next_group(group)) {
1732 /* Walk any ZFS subgroups as well as all standard groups */
1733 if (sa_group_is_zfs(group)) {
1734 sa_group_t subgroup;
1735 for (subgroup = sa_get_sub_group(group);
1736 subgroup != NULL;
1737 subgroup = sa_get_next_group(subgroup)) {
1738 if (check_public(subgroup, skipshare))
1739 return (B_TRUE);
1740 }
1741 } else {
1742 if (check_public(group, skipshare))
1743 return (B_TRUE);
1744 }
1745 }
1746 return (B_FALSE);
1747 }
1748
1749 /*
1750 * sa_enable_share at the protocol level, enable_share must tell the
1751 * implementation that it is to enable the share. This entails
1752 * converting the path and options into the appropriate ioctl
1753 * calls. It is assumed that all error checking of paths, etc. were
1754 * done earlier.
1755 */
1756 static int
1757 nfs_enable_share(sa_share_t share)
1758 {
1759 struct exportdata export;
1760 sa_optionset_t secoptlist;
1761 struct secinfo *sp;
1762 int num_secinfo;
1763 sa_optionset_t opt;
1764 sa_security_t sec;
1765 sa_property_t prop;
1766 char *path;
1767 int err = SA_OK;
1768 int i;
1769 int iszfs;
1770 sa_handle_t handle;
1771
1772 /* Don't drop core if the NFS module isn't loaded. */
1773 (void) signal(SIGSYS, SIG_IGN);
1774
1775 /* get the path since it is important in several places */
1776 path = sa_get_share_attr(share, "path");
1777 if (path == NULL)
1778 return (SA_NO_SUCH_PATH);
1779
1780 iszfs = sa_path_is_zfs(path);
1781 /*
1782 * find the optionsets and security sets. There may not be
1783 * any or there could be one or two for each of optionset and
1784 * security may have multiple, one per security type per
1785 * protocol type.
1786 */
1787 opt = sa_get_derived_optionset(share, "nfs", 1);
1788 secoptlist = (sa_optionset_t)sa_get_all_security_types(share, "nfs", 1);
1789 if (secoptlist != NULL)
1790 num_secinfo = MAX(1, count_security(secoptlist));
1791 else
1792 num_secinfo = 1;
1793
1794 /*
1795 * walk through the options and fill in the structure
1796 * appropriately.
1797 */
1798
1799 (void) memset(&export, '\0', sizeof (export));
1800
1801 /*
1802 * do non-security options first since there is only one after
1803 * the derived group is constructed.
1804 */
1805 export.ex_version = EX_CURRENT_VERSION;
1806 export.ex_anon = UID_NOBODY; /* this is our default value */
1807 export.ex_index = NULL;
1808 export.ex_path = path;
1809 export.ex_pathlen = strlen(path) + 1;
1810
1811 if (opt != NULL)
1812 err = fill_export_from_optionset(&export, opt);
1813
1814 /*
1815 * check to see if "public" is set. If it is, then make sure
1816 * no other share has it set. If it is already used, fail.
1817 */
1818
1819 handle = sa_find_group_handle((sa_group_t)share);
1820 if (export.ex_flags & EX_PUBLIC && public_exists(handle, share)) {
1821 (void) printf(dgettext(TEXT_DOMAIN,
1822 "NFS: Cannot share more than one file "
1823 "system with 'public' property\n"));
1824 err = SA_NOT_ALLOWED;
1825 goto out;
1826 }
1827
1828 sp = calloc(num_secinfo, sizeof (struct secinfo));
1829 if (sp == NULL) {
1830 err = SA_NO_MEMORY;
1831 (void) printf(dgettext(TEXT_DOMAIN,
1832 "NFS: NFS: no memory for security\n"));
1833 goto out;
1834 }
1835 export.ex_secinfo = sp;
1836 /* get default secinfo */
1837 export.ex_seccnt = num_secinfo;
1838 /*
1839 * since we must have one security option defined, we
1840 * init to the default and then override as we find
1841 * defined security options. This handles the case
1842 * where we have no defined options but we need to set
1843 * up one.
1844 */
1845 sp[0].s_window = DEF_WIN;
1846 sp[0].s_rootnames = NULL;
1847 /* setup a default in case no properties defined */
1848 if (nfs_getseconfig_default(&sp[0].s_secinfo)) {
1849 (void) printf(dgettext(TEXT_DOMAIN,
1850 "NFS: nfs_getseconfig_default: failed to "
1851 "get default security mode\n"));
1852 err = SA_CONFIG_ERR;
1853 }
1854 if (secoptlist != NULL) {
1855 for (i = 0, prop = sa_get_property(secoptlist, NULL);
1856 prop != NULL && i < num_secinfo;
1857 prop = sa_get_next_property(prop), i++) {
1858 char *sectype;
1859 sectype = sa_get_property_attr(prop, "type");
1860 /*
1861 * if sectype is NULL, we probably
1862 * have a memory problem and can't get
1863 * the correct values. Rather than
1864 * exporting with incorrect security,
1865 * don't share it.
1866 */
1867 if (sectype == NULL) {
1868 err = SA_NO_MEMORY;
1869 (void) printf(dgettext(TEXT_DOMAIN,
1870 "NFS: Cannot share %s: "
1871 "no memory\n"), path);
1872 goto out;
1873 }
1874 sec = (sa_security_t)sa_get_derived_security(
1875 share, sectype, "nfs", 1);
1876 sp[i].s_window = DEF_WIN;
1877 sp[i].s_rootcnt = 0;
1878 sp[i].s_rootnames = NULL;
1879 (void) fill_security_from_secopts(&sp[i], sec);
1880 if (sec != NULL)
1881 sa_free_derived_security(sec);
1882 if (sectype != NULL)
1883 sa_free_attr_string(sectype);
1884 }
1885 }
1886 /*
1887 * when we get here, we can do the exportfs system call and
1888 * initiate thinsg. We probably want to enable the nfs.server
1889 * service first if it isn't running within SMF.
1890 */
1891 /* check nfs.server status and start if needed */
1892 /* now add the share to the internal tables */
1893 printarg(path, &export);
1894 /*
1895 * call the exportfs system call which is implemented
1896 * via the nfssys() call as the EXPORTFS subfunction.
1897 */
1898 if (iszfs) {
1899 struct exportfs_args ea;
1900 share_t sh;
1901 char *str;
1902 priv_set_t *priv_effective;
1903 int privileged;
1904
1905 /*
1906 * If we aren't a privileged user
1907 * and NFS server service isn't running
1908 * then print out an error message
1909 * and return EPERM
1910 */
1911
1912 priv_effective = priv_allocset();
1913 (void) getppriv(PRIV_EFFECTIVE, priv_effective);
1914
1915 privileged = (priv_isfullset(priv_effective) == B_TRUE);
1916 priv_freeset(priv_effective);
1917
1918 if (!privileged &&
1919 (str = smf_get_state(NFS_SERVER_SVC)) != NULL) {
1920 err = 0;
1921 if (strcmp(str, SCF_STATE_STRING_ONLINE) != 0) {
1922 (void) printf(dgettext(TEXT_DOMAIN,
1923 "NFS: Cannot share remote "
1924 "filesystem: %s\n"), path);
1925 (void) printf(dgettext(TEXT_DOMAIN,
1926 "NFS: Service needs to be enabled "
1927 "by a privileged user\n"));
1928 err = SA_SYSTEM_ERR;
1929 errno = EPERM;
1930 }
1931 free(str);
1932 }
1933
1934 if (err == 0) {
1935 ea.dname = path;
1936 ea.uex = &export;
1937
1938 (void) sa_sharetab_fill_zfs(share, &sh, "nfs");
1939 err = sa_share_zfs(share, NULL, path, &sh,
1940 &ea, ZFS_SHARE_NFS);
1941 if (err != SA_OK) {
1942 errno = err;
1943 err = -1;
1944 }
1945 sa_emptyshare(&sh);
1946 }
1947 } else {
1948 err = exportfs(path, &export);
1949 }
1950
1951 if (err < 0) {
1952 err = SA_SYSTEM_ERR;
1953 switch (errno) {
1954 case EREMOTE:
1955 (void) printf(dgettext(TEXT_DOMAIN,
1956 "NFS: Cannot share filesystems "
1957 "in non-global zones: %s\n"), path);
1958 err = SA_NOT_SUPPORTED;
1959 break;
1960 case EPERM:
1961 if (getzoneid() != GLOBAL_ZONEID) {
1962 (void) printf(dgettext(TEXT_DOMAIN,
1963 "NFS: Cannot share file systems "
1964 "in non-global zones: %s\n"), path);
1965 err = SA_NOT_SUPPORTED;
1966 break;
1967 }
1968 err = SA_NO_PERMISSION;
1969 break;
1970 case EEXIST:
1971 err = SA_SHARE_EXISTS;
1972 break;
1973 default:
1974 break;
1975 }
1976 } else {
1977 /* update sharetab with an add/modify */
1978 if (!iszfs) {
1979 (void) sa_update_sharetab(share, "nfs");
1980 }
1981 }
1982
1983 if (err == SA_OK) {
1984 /*
1985 * enable services as needed. This should probably be
1986 * done elsewhere in order to minimize the calls to
1987 * check services.
1988 */
1989 /*
1990 * check to see if logging and other services need to
1991 * be triggered, but only if there wasn't an
1992 * error. This is probably where sharetab should be
1993 * updated with the NFS specific entry.
1994 */
1995 if (export.ex_flags & EX_LOG) {
1996 /* enable logging */
1997 if (nfslogtab_add(path, export.ex_log_buffer,
1998 export.ex_tag) != 0) {
1999 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2000 "Could not enable logging for %s\n"),
2001 path);
2002 }
2003 _check_services(service_list_logging);
2004 } else {
2005 /*
2006 * don't have logging so remove it from file. It might
2007 * not be thre, but that doesn't matter.
2008 */
2009 (void) nfslogtab_deactivate(path);
2010 _check_services(service_list_default);
2011 }
2012 }
2013
2014 out:
2015 if (path != NULL)
2016 free(path);
2017
2018 cleanup_export(&export);
2019 if (opt != NULL)
2020 sa_free_derived_optionset(opt);
2021 if (secoptlist != NULL)
2022 (void) sa_destroy_optionset(secoptlist);
2023 return (err);
2024 }
2025
2026 /*
2027 * nfs_disable_share(share, path)
2028 *
2029 * Unshare the specified share. Note that "path" is the same path as
2030 * what is in the "share" object. It is passed in to avoid an
2031 * additional lookup. A missing "path" value makes this a no-op
2032 * function.
2033 */
2034 static int
2035 nfs_disable_share(sa_share_t share, char *path)
2036 {
2037 int err;
2038 int ret = SA_OK;
2039 int iszfs;
2040 sa_group_t parent;
2041 sa_handle_t handle;
2042
2043 if (path == NULL)
2044 return (ret);
2045
2046 /*
2047 * If the share is in a ZFS group we need to handle it
2048 * differently. Just being on a ZFS file system isn't
2049 * enough since we may be in a legacy share case.
2050 */
2051 parent = sa_get_parent_group(share);
2052 iszfs = sa_group_is_zfs(parent);
2053 if (iszfs) {
2054 struct exportfs_args ea;
2055 share_t sh = { 0 };
2056 ea.dname = path;
2057 ea.uex = NULL;
2058 sh.sh_path = path;
2059 sh.sh_fstype = "nfs";
2060
2061 err = sa_share_zfs(share, NULL, path, &sh,
2062 &ea, ZFS_UNSHARE_NFS);
2063 if (err != SA_OK) {
2064 errno = err;
2065 err = -1;
2066 }
2067 } else {
2068 err = exportfs(path, NULL);
2069 }
2070 if (err < 0) {
2071 /*
2072 * TBD: only an error in some
2073 * cases - need better analysis
2074 */
2075 switch (errno) {
2076 case EPERM:
2077 case EACCES:
2078 ret = SA_NO_PERMISSION;
2079 if (getzoneid() != GLOBAL_ZONEID) {
2080 ret = SA_NOT_SUPPORTED;
2081 }
2082 break;
2083 case EINVAL:
2084 case ENOENT:
2085 ret = SA_NO_SUCH_PATH;
2086 break;
2087 default:
2088 ret = SA_SYSTEM_ERR;
2089 break;
2090 }
2091 }
2092 if (ret == SA_OK || ret == SA_NO_SUCH_PATH) {
2093 handle = sa_find_group_handle((sa_group_t)share);
2094 if (!iszfs)
2095 (void) sa_delete_sharetab(handle, path, "nfs");
2096 /* just in case it was logged */
2097 (void) nfslogtab_deactivate(path);
2098 }
2099 return (ret);
2100 }
2101
2102 /*
2103 * check_rorwnone(v1, v2, v3)
2104 *
2105 * check ro vs rw vs none values. Over time this may get beefed up.
2106 * for now it just does simple checks. v1 is never NULL but v2 or v3
2107 * could be.
2108 */
2109
2110 static int
2111 check_rorwnone(char *v1, char *v2, char *v3)
2112 {
2113 int ret = SA_OK;
2114 if (v2 != NULL && strcmp(v1, v2) == 0)
2115 ret = SA_VALUE_CONFLICT;
2116 else if (v3 != NULL && strcmp(v1, v3) == 0)
2117 ret = SA_VALUE_CONFLICT;
2118
2119 return (ret);
2120 }
2121
2122 /*
2123 * nfs_validate_property(handle, property, parent)
2124 *
2125 * Check that the property has a legitimate value for its type.
2126 */
2127
2128 static int
2129 nfs_validate_property(sa_handle_t handle, sa_property_t property,
2130 sa_optionset_t parent)
2131 {
2132 int ret = SA_OK;
2133 char *propname;
2134 char *other1;
2135 char *other2;
2136 int optindex;
2137 nfsl_config_t *configlist;
2138 sa_group_t parent_group;
2139 char *value;
2140
2141 propname = sa_get_property_attr(property, "type");
2142
2143 if ((optindex = findopt(propname)) < 0)
2144 ret = SA_NO_SUCH_PROP;
2145
2146 /* need to validate value range here as well */
2147
2148 if (ret == SA_OK) {
2149 parent_group = sa_get_parent_group((sa_share_t)parent);
2150 if (optdefs[optindex].share && parent_group != NULL &&
2151 !sa_is_share(parent_group))
2152 ret = SA_PROP_SHARE_ONLY;
2153 }
2154 if (ret == SA_OK) {
2155 if (optdefs[optindex].index == OPT_PUBLIC) {
2156 /*
2157 * Public is special in that only one instance can
2158 * be in the repository at the same time.
2159 */
2160 if (public_exists(handle, parent_group)) {
2161 sa_free_attr_string(propname);
2162 return (SA_VALUE_CONFLICT);
2163 }
2164 }
2165 value = sa_get_property_attr(property, "value");
2166 if (value != NULL) {
2167 /* first basic type checking */
2168 switch (optdefs[optindex].type) {
2169 case OPT_TYPE_NUMBER:
2170 /* check that the value is all digits */
2171 if (!is_a_number(value))
2172 ret = SA_BAD_VALUE;
2173 break;
2174 case OPT_TYPE_BOOLEAN:
2175 if (strlen(value) == 0 ||
2176 strcasecmp(value, "true") == 0 ||
2177 strcmp(value, "1") == 0 ||
2178 strcasecmp(value, "false") == 0 ||
2179 strcmp(value, "0") == 0) {
2180 ret = SA_OK;
2181 } else {
2182 ret = SA_BAD_VALUE;
2183 }
2184 break;
2185 case OPT_TYPE_USER:
2186 if (!is_a_number(value)) {
2187 struct passwd *pw;
2188 /*
2189 * in this case it would have to be a
2190 * user name
2191 */
2192 pw = getpwnam(value);
2193 if (pw == NULL)
2194 ret = SA_BAD_VALUE;
2195 endpwent();
2196 } else {
2197 uint64_t intval;
2198 intval = strtoull(value, NULL, 0);
2199 if (intval > UID_MAX && intval != ~0)
2200 ret = SA_BAD_VALUE;
2201 }
2202 break;
2203 case OPT_TYPE_FILE:
2204 if (strcmp(value, "..") == 0 ||
2205 strchr(value, '/') != NULL) {
2206 ret = SA_BAD_VALUE;
2207 }
2208 break;
2209 case OPT_TYPE_ACCLIST: {
2210 sa_property_t oprop1;
2211 sa_property_t oprop2;
2212 char *ovalue1 = NULL;
2213 char *ovalue2 = NULL;
2214
2215 if (parent == NULL)
2216 break;
2217 /*
2218 * access list handling. Should eventually
2219 * validate that all the values make sense.
2220 * Also, ro and rw may have cross value
2221 * conflicts.
2222 */
2223 if (strcmp(propname, SHOPT_RO) == 0) {
2224 other1 = SHOPT_RW;
2225 other2 = SHOPT_NONE;
2226 } else if (strcmp(propname, SHOPT_RW) == 0) {
2227 other1 = SHOPT_RO;
2228 other2 = SHOPT_NONE;
2229 } else if (strcmp(propname, SHOPT_NONE) == 0) {
2230 other1 = SHOPT_RO;
2231 other2 = SHOPT_RW;
2232 } else {
2233 other1 = NULL;
2234 other2 = NULL;
2235 }
2236 if (other1 == NULL && other2 == NULL)
2237 break;
2238
2239 /* compare rw(ro) with ro(rw) */
2240
2241 oprop1 = sa_get_property(parent, other1);
2242 oprop2 = sa_get_property(parent, other2);
2243 if (oprop1 == NULL && oprop2 == NULL)
2244 break;
2245 /*
2246 * Only potential confusion if other1
2247 * or other2 exists. Check the values
2248 * and run the check if there is a
2249 * value other than the one we are
2250 * explicitly looking at.
2251 */
2252 ovalue1 = sa_get_property_attr(oprop1, "value");
2253 ovalue2 = sa_get_property_attr(oprop2, "value");
2254 if (ovalue1 != NULL || ovalue2 != NULL)
2255 ret = check_rorwnone(value, ovalue1,
2256 ovalue2);
2257
2258 if (ovalue1 != NULL)
2259 sa_free_attr_string(ovalue1);
2260 if (ovalue2 != NULL)
2261 sa_free_attr_string(ovalue2);
2262 break;
2263 }
2264 case OPT_TYPE_LOGTAG:
2265 if (nfsl_getconfig_list(&configlist) == 0) {
2266 int error;
2267 if (value == NULL ||
2268 strlen(value) == 0) {
2269 if (value != NULL)
2270 sa_free_attr_string(
2271 value);
2272 value = strdup("global");
2273 }
2274 if (value != NULL &&
2275 nfsl_findconfig(configlist, value,
2276 &error) == NULL) {
2277 ret = SA_BAD_VALUE;
2278 }
2279 /* Must always free when done */
2280 nfsl_freeconfig_list(&configlist);
2281 } else {
2282 ret = SA_CONFIG_ERR;
2283 }
2284 break;
2285 case OPT_TYPE_STRING:
2286 /* whatever is here should be ok */
2287 break;
2288 case OPT_TYPE_SECURITY:
2289 /*
2290 * The "sec" property isn't used in the
2291 * non-legacy parts of sharemgr. We need to
2292 * reject it here. For legacy, it is pulled
2293 * out well before we get here.
2294 */
2295 ret = SA_NO_SUCH_PROP;
2296 break;
2297 default:
2298 break;
2299 }
2300
2301 if (value != NULL)
2302 sa_free_attr_string(value);
2303
2304 if (ret == SA_OK && optdefs[optindex].check != NULL) {
2305 /* do the property specific check */
2306 ret = optdefs[optindex].check(handle, property);
2307 }
2308 }
2309 }
2310
2311 if (propname != NULL)
2312 sa_free_attr_string(propname);
2313 return (ret);
2314 }
2315
2316 /*
2317 * Protocol management functions
2318 *
2319 * Properties defined in the default files are defined in
2320 * proto_option_defs for parsing and validation. If "other" and
2321 * "compare" are set, then the value for this property should be
2322 * compared against the property specified in "other" using the
2323 * "compare" check (either <= or >=) in order to ensure that the
2324 * values are in the correct range. E.g. setting server_versmin
2325 * higher than server_versmax should not be allowed.
2326 */
2327
2328 struct proto_option_defs {
2329 char *tag;
2330 char *name; /* display name -- remove protocol identifier */
2331 int index;
2332 int type;
2333 union {
2334 int intval;
2335 char *string;
2336 } defvalue;
2337 uint32_t svcs;
2338 int32_t minval;
2339 int32_t maxval;
2340 char *other;
2341 int compare;
2342 #define OPT_CMP_GE 0
2343 #define OPT_CMP_LE 1
2344 int (*check)(char *);
2345 } proto_options[] = {
2346 #define PROTO_OPT_NFSD_SERVERS 0
2347 {"nfsd_servers",
2348 "servers", PROTO_OPT_NFSD_SERVERS, OPT_TYPE_NUMBER, 16, SVC_NFSD,
2349 1, INT32_MAX},
2350 #define PROTO_OPT_LOCKD_LISTEN_BACKLOG 1
2351 {"lockd_listen_backlog",
2352 "lockd_listen_backlog", PROTO_OPT_LOCKD_LISTEN_BACKLOG,
2353 OPT_TYPE_NUMBER, 32, SVC_LOCKD, 32, INT32_MAX},
2354 #define PROTO_OPT_LOCKD_SERVERS 2
2355 {"lockd_servers",
2356 "lockd_servers", PROTO_OPT_LOCKD_SERVERS, OPT_TYPE_NUMBER, 20,
2357 SVC_LOCKD, 1, INT32_MAX},
2358 #define PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT 3
2359 {"lockd_retransmit_timeout",
2360 "lockd_retransmit_timeout", PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT,
2361 OPT_TYPE_NUMBER, 5, SVC_LOCKD, 0, INT32_MAX},
2362 #define PROTO_OPT_GRACE_PERIOD 4
2363 {"grace_period",
2364 "grace_period", PROTO_OPT_GRACE_PERIOD, OPT_TYPE_NUMBER, 90,
2365 SVC_LOCKD, 0, INT32_MAX},
2366 #define PROTO_OPT_NFS_SERVER_VERSMIN 5
2367 {"nfs_server_versmin",
2368 "server_versmin", PROTO_OPT_NFS_SERVER_VERSMIN, OPT_TYPE_NUMBER,
2369 (int)NFS_VERSMIN_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN,
2370 NFS_VERSMAX, "server_versmax", OPT_CMP_LE},
2371 #define PROTO_OPT_NFS_SERVER_VERSMAX 6
2372 {"nfs_server_versmax",
2373 "server_versmax", PROTO_OPT_NFS_SERVER_VERSMAX, OPT_TYPE_NUMBER,
2374 (int)NFS_VERSMAX_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN,
2375 NFS_VERSMAX, "server_versmin", OPT_CMP_GE},
2376 #define PROTO_OPT_NFS_CLIENT_VERSMIN 7
2377 {"nfs_client_versmin",
2378 "client_versmin", PROTO_OPT_NFS_CLIENT_VERSMIN, OPT_TYPE_NUMBER,
2379 (int)NFS_VERSMIN_DEFAULT, SVC_CLIENT, NFS_VERSMIN, NFS_VERSMAX,
2380 "client_versmax", OPT_CMP_LE},
2381 #define PROTO_OPT_NFS_CLIENT_VERSMAX 8
2382 {"nfs_client_versmax",
2383 "client_versmax", PROTO_OPT_NFS_CLIENT_VERSMAX, OPT_TYPE_NUMBER,
2384 (int)NFS_VERSMAX_DEFAULT, SVC_CLIENT, NFS_VERSMIN, NFS_VERSMAX,
2385 "client_versmin", OPT_CMP_GE},
2386 #define PROTO_OPT_NFS_SERVER_DELEGATION 9
2387 {"nfs_server_delegation",
2388 "server_delegation", PROTO_OPT_NFS_SERVER_DELEGATION,
2389 OPT_TYPE_ONOFF, NFS_SERVER_DELEGATION_DEFAULT, SVC_NFSD, 0, 0},
2390 #define PROTO_OPT_NFSMAPID_DOMAIN 10
2391 {"nfsmapid_domain",
2392 "nfsmapid_domain", PROTO_OPT_NFSMAPID_DOMAIN, OPT_TYPE_DOMAIN,
2393 NULL, SVC_NFSMAPID, 0, 0},
2394 #define PROTO_OPT_NFSD_MAX_CONNECTIONS 11
2395 {"nfsd_max_connections",
2396 "max_connections", PROTO_OPT_NFSD_MAX_CONNECTIONS,
2397 OPT_TYPE_NUMBER, -1, SVC_NFSD, -1, INT32_MAX},
2398 #define PROTO_OPT_NFSD_PROTOCOL 12
2399 {"nfsd_protocol",
2400 "protocol", PROTO_OPT_NFSD_PROTOCOL, OPT_TYPE_PROTOCOL, 0,
2401 SVC_NFSD, 0, 0},
2402 #define PROTO_OPT_NFSD_LISTEN_BACKLOG 13
2403 {"nfsd_listen_backlog",
2404 "listen_backlog", PROTO_OPT_NFSD_LISTEN_BACKLOG,
2405 OPT_TYPE_NUMBER, 0, SVC_NFSD, 0, INT32_MAX},
2406 #define PROTO_OPT_NFSD_DEVICE 14
2407 {"nfsd_device",
2408 "device", PROTO_OPT_NFSD_DEVICE,
2409 OPT_TYPE_STRING, NULL, SVC_NFSD, 0, 0},
2410 {NULL}
2411 };
2412
2413 /*
2414 * the protoset holds the defined options so we don't have to read
2415 * them multiple times
2416 */
2417 static sa_protocol_properties_t protoset;
2418
2419 static int
2420 findprotoopt(char *name, int whichname)
2421 {
2422 int i;
2423 for (i = 0; proto_options[i].tag != NULL; i++) {
2424 if (whichname == 1) {
2425 if (strcasecmp(proto_options[i].name, name) == 0)
2426 return (i);
2427 } else {
2428 if (strcasecmp(proto_options[i].tag, name) == 0)
2429 return (i);
2430 }
2431 }
2432 return (-1);
2433 }
2434
2435 /*
2436 * fixcaselower(str)
2437 *
2438 * convert a string to lower case (inplace).
2439 */
2440
2441 static void
2442 fixcaselower(char *str)
2443 {
2444 while (*str) {
2445 *str = tolower(*str);
2446 str++;
2447 }
2448 }
2449
2450 /*
2451 * skipwhitespace(str)
2452 *
2453 * Skip leading white space. It is assumed that it is called with a
2454 * valid pointer.
2455 */
2456
2457 static char *
2458 skipwhitespace(char *str)
2459 {
2460 while (*str && isspace(*str))
2461 str++;
2462
2463 return (str);
2464 }
2465
2466 /*
2467 * extractprop()
2468 *
2469 * Extract the property and value out of the line and create the
2470 * property in the optionset.
2471 */
2472 static int
2473 extractprop(char *name, char *value)
2474 {
2475 sa_property_t prop;
2476 int index;
2477 int ret = SA_OK;
2478 /*
2479 * Remove any leading
2480 * white space.
2481 */
2482 name = skipwhitespace(name);
2483
2484 index = findprotoopt(name, 1);
2485 if (index >= 0) {
2486 fixcaselower(name);
2487 prop = sa_create_property(proto_options[index].name, value);
2488 if (prop != NULL)
2489 ret = sa_add_protocol_property(protoset, prop);
2490 else
2491 ret = SA_NO_MEMORY;
2492 }
2493 return (ret);
2494 }
2495
2496 scf_type_t
2497 getscftype(int type)
2498 {
2499 scf_type_t ret;
2500
2501 switch (type) {
2502 case OPT_TYPE_NUMBER:
2503 ret = SCF_TYPE_INTEGER;
2504 break;
2505 case OPT_TYPE_BOOLEAN:
2506 ret = SCF_TYPE_BOOLEAN;
2507 break;
2508 default:
2509 ret = SCF_TYPE_ASTRING;
2510 }
2511 return (ret);
2512 }
2513
2514 char *
2515 getsvcname(uint32_t svcs)
2516 {
2517 char *service;
2518 switch (svcs) {
2519 case SVC_LOCKD:
2520 service = LOCKD;
2521 break;
2522 case SVC_STATD:
2523 service = STATD;
2524 break;
2525 case SVC_NFSD:
2526 service = NFSD;
2527 break;
2528 case SVC_CLIENT:
2529 service = NFS_CLIENT_SVC;
2530 break;
2531 case SVC_NFS4CBD:
2532 service = NFS4CBD;
2533 break;
2534 case SVC_NFSMAPID:
2535 service = NFSMAPID;
2536 break;
2537 case SVC_RQUOTAD:
2538 service = RQUOTAD;
2539 break;
2540 case SVC_NFSLOGD:
2541 service = NFSLOGD;
2542 break;
2543 case SVC_REPARSED:
2544 service = REPARSED;
2545 break;
2546 default:
2547 service = NFSD;
2548 }
2549 return (service);
2550 }
2551
2552 /*
2553 * initprotofromsmf()
2554 *
2555 * Read NFS SMF properties and add the defined values to the
2556 * protoset. Note that default values are known from the built in
2557 * table in case SMF doesn't have a definition. Not having
2558 * SMF properties is OK since we have builtin default
2559 * values.
2560 */
2561 static int
2562 initprotofromsmf()
2563 {
2564 char name[PATH_MAX];
2565 char value[PATH_MAX];
2566 int ret = SA_OK, bufsz = 0, i;
2567
2568 protoset = sa_create_protocol_properties("nfs");
2569 if (protoset != NULL) {
2570 for (i = 0; proto_options[i].tag != NULL; i++) {
2571 scf_type_t ptype;
2572 char *svc_name;
2573
2574 bzero(value, PATH_MAX);
2575 (void) strncpy(name, proto_options[i].name, PATH_MAX);
2576 /* Replace NULL with the correct instance */
2577 ptype = getscftype(proto_options[i].type);
2578 svc_name = getsvcname(proto_options[i].svcs);
2579 bufsz = PATH_MAX;
2580 ret = nfs_smf_get_prop(name, value,
2581 (char *)DEFAULT_INSTANCE, ptype,
2582 svc_name, &bufsz);
2583 if (ret == SA_OK) {
2584 ret = extractprop(name, value);
2585 }
2586 }
2587 } else {
2588 ret = SA_NO_MEMORY;
2589 }
2590
2591 return (ret);
2592 }
2593
2594 /*
2595 * add_defaults()
2596 *
2597 * Add the default values for any property not defined
2598 * in NFS SMF repository.
2599 * Values are set according to their defined types.
2600 */
2601
2602 static void
2603 add_defaults()
2604 {
2605 int i;
2606 char number[MAXDIGITS];
2607
2608 for (i = 0; proto_options[i].tag != NULL; i++) {
2609 sa_property_t prop;
2610 prop = sa_get_protocol_property(protoset,
2611 proto_options[i].name);
2612 if (prop == NULL) {
2613 /* add the default value */
2614 switch (proto_options[i].type) {
2615 case OPT_TYPE_NUMBER:
2616 (void) snprintf(number, sizeof (number), "%d",
2617 proto_options[i].defvalue.intval);
2618 prop = sa_create_property(proto_options[i].name,
2619 number);
2620 break;
2621
2622 case OPT_TYPE_BOOLEAN:
2623 prop = sa_create_property(proto_options[i].name,
2624 proto_options[i].defvalue.intval ?
2625 "true" : "false");
2626 break;
2627
2628 case OPT_TYPE_ONOFF:
2629 prop = sa_create_property(proto_options[i].name,
2630 proto_options[i].defvalue.intval ?
2631 "on" : "off");
2632 break;
2633
2634 default:
2635 /* treat as strings of zero length */
2636 prop = sa_create_property(proto_options[i].name,
2637 "");
2638 break;
2639 }
2640 if (prop != NULL)
2641 (void) sa_add_protocol_property(protoset, prop);
2642 }
2643 }
2644 }
2645
2646 static void
2647 free_protoprops()
2648 {
2649 if (protoset != NULL) {
2650 xmlFreeNode(protoset);
2651 protoset = NULL;
2652 }
2653 }
2654
2655 /*
2656 * nfs_init()
2657 *
2658 * Initialize the NFS plugin.
2659 */
2660
2661 static int
2662 nfs_init()
2663 {
2664 int ret = SA_OK;
2665
2666 if (sa_plugin_ops.sa_init != nfs_init) {
2667 (void) printf(dgettext(TEXT_DOMAIN,
2668 "NFS plugin not properly initialized\n"));
2669 return (SA_CONFIG_ERR);
2670 }
2671
2672 ret = initprotofromsmf();
2673 if (ret != SA_OK) {
2674 (void) printf(dgettext(TEXT_DOMAIN,
2675 "NFS plugin problem with SMF repository: %s\n"),
2676 sa_errorstr(ret));
2677 ret = SA_OK;
2678 }
2679 add_defaults();
2680
2681 return (ret);
2682 }
2683
2684 /*
2685 * nfs_fini()
2686 *
2687 * uninitialize the NFS plugin. Want to avoid memory leaks.
2688 */
2689
2690 static void
2691 nfs_fini()
2692 {
2693 free_protoprops();
2694 }
2695
2696 /*
2697 * nfs_get_proto_set()
2698 *
2699 * Return an optionset with all the protocol specific properties in
2700 * it.
2701 */
2702
2703 static sa_protocol_properties_t
2704 nfs_get_proto_set()
2705 {
2706 return (protoset);
2707 }
2708
2709 /*
2710 * service_in_state(service, chkstate)
2711 *
2712 * Want to know if the specified service is in the desired state
2713 * (chkstate) or not. Return true (1) if it is and false (0) if it
2714 * isn't.
2715 */
2716 static int
2717 service_in_state(char *service, const char *chkstate)
2718 {
2719 char *state;
2720 int ret = B_FALSE;
2721
2722 state = smf_get_state(service);
2723 if (state != NULL) {
2724 /* got the state so get the equality for the return value */
2725 ret = strcmp(state, chkstate) == 0 ? B_TRUE : B_FALSE;
2726 free(state);
2727 }
2728 return (ret);
2729 }
2730
2731 /*
2732 * restart_service(svcs)
2733 *
2734 * Walk through the bit mask of services that need to be restarted in
2735 * order to use the new property values. Some properties affect
2736 * multiple daemons. Should only restart a service if it is currently
2737 * enabled (online).
2738 */
2739
2740 static void
2741 restart_service(uint32_t svcs)
2742 {
2743 uint32_t mask;
2744 int ret;
2745 char *service;
2746
2747 for (mask = 1; svcs != 0; mask <<= 1) {
2748 switch (svcs & mask) {
2749 case SVC_LOCKD:
2750 service = LOCKD;
2751 break;
2752 case SVC_STATD:
2753 service = STATD;
2754 break;
2755 case SVC_NFSD:
2756 service = NFSD;
2757 break;
2758 case SVC_MOUNTD:
2759 service = MOUNTD;
2760 break;
2761 case SVC_NFS4CBD:
2762 service = NFS4CBD;
2763 break;
2764 case SVC_NFSMAPID:
2765 service = NFSMAPID;
2766 break;
2767 case SVC_RQUOTAD:
2768 service = RQUOTAD;
2769 break;
2770 case SVC_NFSLOGD:
2771 service = NFSLOGD;
2772 break;
2773 case SVC_REPARSED:
2774 service = REPARSED;
2775 break;
2776 case SVC_CLIENT:
2777 service = NFS_CLIENT_SVC;
2778 break;
2779 default:
2780 continue;
2781 }
2782
2783 /*
2784 * Only attempt to restart the service if it is
2785 * currently running. In the future, it may be
2786 * desirable to use smf_refresh_instance if the NFS
2787 * services ever implement the refresh method.
2788 */
2789 if (service_in_state(service, SCF_STATE_STRING_ONLINE)) {
2790 ret = smf_restart_instance(service);
2791 /*
2792 * There are only a few SMF errors at this point, but
2793 * it is also possible that a bad value may have put
2794 * the service into maintenance if there wasn't an
2795 * SMF level error.
2796 */
2797 if (ret != 0) {
2798 (void) fprintf(stderr,
2799 dgettext(TEXT_DOMAIN,
2800 "%s failed to restart: %s\n"),
2801 scf_strerror(scf_error()));
2802 } else {
2803 /*
2804 * Check whether it has gone to "maintenance"
2805 * mode or not. Maintenance implies something
2806 * went wrong.
2807 */
2808 if (service_in_state(service,
2809 SCF_STATE_STRING_MAINT)) {
2810 (void) fprintf(stderr,
2811 dgettext(TEXT_DOMAIN,
2812 "%s failed to restart\n"),
2813 service);
2814 }
2815 }
2816 }
2817 svcs &= ~mask;
2818 }
2819 }
2820
2821 /*
2822 * nfs_minmax_check(name, value)
2823 *
2824 * Verify that the value for the property specified by index is valid
2825 * relative to the opposite value in the case of a min/max variable.
2826 * Currently, server_minvers/server_maxvers and
2827 * client_minvers/client_maxvers are the only ones to check.
2828 */
2829
2830 static int
2831 nfs_minmax_check(int index, int value)
2832 {
2833 int val;
2834 char *pval;
2835 sa_property_t prop;
2836 sa_optionset_t opts;
2837 int ret = B_TRUE;
2838
2839 if (proto_options[index].other != NULL) {
2840 /* have a property to compare against */
2841 opts = nfs_get_proto_set();
2842 prop = sa_get_property(opts, proto_options[index].other);
2843 /*
2844 * If we don't find the property, assume default
2845 * values which will work since the max will be at the
2846 * max and the min at the min.
2847 */
2848 if (prop != NULL) {
2849 pval = sa_get_property_attr(prop, "value");
2850 if (pval != NULL) {
2851 val = strtoul(pval, NULL, 0);
2852 if (proto_options[index].compare ==
2853 OPT_CMP_LE) {
2854 ret = value <= val ? B_TRUE : B_FALSE;
2855 } else if (proto_options[index].compare ==
2856 OPT_CMP_GE) {
2857 ret = value >= val ? B_TRUE : B_FALSE;
2858 }
2859 sa_free_attr_string(pval);
2860 }
2861 }
2862 }
2863 return (ret);
2864 }
2865
2866 /*
2867 * nfs_validate_proto_prop(index, name, value)
2868 *
2869 * Verify that the property specified by name can take the new
2870 * value. This is a sanity check to prevent bad values getting into
2871 * the default files. All values need to be checked against what is
2872 * allowed by their defined type. If a type isn't explicitly defined
2873 * here, it is treated as a string.
2874 *
2875 * Note that OPT_TYPE_NUMBER will additionally check that the value is
2876 * within the range specified and potentially against another property
2877 * value as well as specified in the proto_options members other and
2878 * compare.
2879 */
2880
2881 static int
2882 nfs_validate_proto_prop(int index, char *name, char *value)
2883 {
2884 int ret = SA_OK;
2885 char *cp;
2886 #ifdef lint
2887 name = name;
2888 #endif
2889 switch (proto_options[index].type) {
2890 case OPT_TYPE_NUMBER:
2891 if (!is_a_number(value))
2892 ret = SA_BAD_VALUE;
2893 else {
2894 int val;
2895 val = strtoul(value, NULL, 0);
2896 if (val < proto_options[index].minval ||
2897 val > proto_options[index].maxval)
2898 ret = SA_BAD_VALUE;
2899 /*
2900 * For server_versmin/server_versmax and
2901 * client_versmin/client_versmax, the value of the
2902 * min(max) should be checked to be correct relative
2903 * to the current max(min).
2904 */
2905 if (!nfs_minmax_check(index, val)) {
2906 ret = SA_BAD_VALUE;
2907 }
2908 }
2909 break;
2910
2911 case OPT_TYPE_DOMAIN:
2912 /*
2913 * needs to be a qualified domain so will have at
2914 * least one period and other characters on either
2915 * side of it. A zero length string is also allowed
2916 * and is the way to turn off the override.
2917 */
2918 if (strlen(value) == 0)
2919 break;
2920 cp = strchr(value, '.');
2921 if (cp == NULL || cp == value || strchr(value, '@') != NULL)
2922 ret = SA_BAD_VALUE;
2923 break;
2924
2925 case OPT_TYPE_BOOLEAN:
2926 if (strlen(value) == 0 ||
2927 strcasecmp(value, "true") == 0 ||
2928 strcmp(value, "1") == 0 ||
2929 strcasecmp(value, "false") == 0 ||
2930 strcmp(value, "0") == 0) {
2931 ret = SA_OK;
2932 } else {
2933 ret = SA_BAD_VALUE;
2934 }
2935 break;
2936
2937 case OPT_TYPE_ONOFF:
2938 if (strcasecmp(value, "on") != 0 &&
2939 strcasecmp(value, "off") != 0) {
2940 ret = SA_BAD_VALUE;
2941 }
2942 break;
2943
2944 case OPT_TYPE_PROTOCOL:
2945 if (strlen(value) != 0 &&
2946 strcasecmp(value, "all") != 0 &&
2947 strcasecmp(value, "tcp") != 0 &&
2948 strcasecmp(value, "udp") != 0)
2949 ret = SA_BAD_VALUE;
2950 break;
2951
2952 default:
2953 /* treat as a string */
2954 break;
2955 }
2956 return (ret);
2957 }
2958
2959 /*
2960 * nfs_set_proto_prop(prop)
2961 *
2962 * check that prop is valid.
2963 */
2964
2965 static int
2966 nfs_set_proto_prop(sa_property_t prop)
2967 {
2968 int ret = SA_OK;
2969 char *name;
2970 char *value;
2971
2972 name = sa_get_property_attr(prop, "type");
2973 value = sa_get_property_attr(prop, "value");
2974 if (name != NULL && value != NULL) {
2975 scf_type_t sctype;
2976 char *svc_name;
2977 char *instance = NULL;
2978 int index = findprotoopt(name, 1);
2979
2980 ret = nfs_validate_proto_prop(index, name, value);
2981 if (ret == SA_OK) {
2982 sctype = getscftype(proto_options[index].type);
2983 svc_name = getsvcname(proto_options[index].svcs);
2984 if (sctype == SCF_TYPE_BOOLEAN) {
2985 if (value != NULL)
2986 sa_free_attr_string(value);
2987 if (string_to_boolean(value) == 0)
2988 value = strdup("0");
2989 else
2990 value = strdup("1");
2991 }
2992 ret = nfs_smf_set_prop(name, value, instance, sctype,
2993 svc_name);
2994 if (ret == SA_OK) {
2995 restart_service(proto_options[index].svcs);
2996 } else {
2997 (void) printf(dgettext(TEXT_DOMAIN,
2998 "Cannot restart NFS services : %s\n"),
2999 sa_errorstr(ret));
3000 }
3001 }
3002 }
3003 if (name != NULL)
3004 sa_free_attr_string(name);
3005 if (value != NULL)
3006 sa_free_attr_string(value);
3007 return (ret);
3008 }
3009
3010 /*
3011 * nfs_get_status()
3012 *
3013 * What is the current status of the nfsd? We use the SMF state here.
3014 * Caller must free the returned value.
3015 */
3016
3017 static char *
3018 nfs_get_status()
3019 {
3020 char *state;
3021 state = smf_get_state(NFSD);
3022 return (state != NULL ? state : strdup("-"));
3023 }
3024
3025 /*
3026 * nfs_space_alias(alias)
3027 *
3028 * Lookup the space (security) name. If it is default, convert to the
3029 * real name.
3030 */
3031
3032 static char *
3033 nfs_space_alias(char *space)
3034 {
3035 char *name = space;
3036 seconfig_t secconf;
3037
3038 /*
3039 * Only the space named "default" is special. If it is used,
3040 * the default needs to be looked up and the real name used.
3041 * This is normally "sys" but could be changed. We always
3042 * change defautl to the real name.
3043 */
3044 if (strcmp(space, "default") == 0 &&
3045 nfs_getseconfig_default(&secconf) == 0) {
3046 if (nfs_getseconfig_bynumber(secconf.sc_nfsnum, &secconf) == 0)
3047 name = secconf.sc_name;
3048 }
3049 return (strdup(name));
3050 }
3051
3052 /*
3053 * nfs_features()
3054 *
3055 * Return a mask of the features required.
3056 */
3057
3058 static uint64_t
3059 nfs_features()
3060 {
3061 return ((uint64_t)SA_FEATURE_DFSTAB | SA_FEATURE_SERVER);
3062 }