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