1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25
26 /*
27 * Door server routines for nfsmapid daemon
28 * Translate NFSv4 users and groups between numeric and string values
29 */
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <alloca.h>
33 #include <signal.h>
34 #include <libintl.h>
35 #include <limits.h>
36 #include <errno.h>
37 #include <sys/types.h>
38 #include <string.h>
39 #include <memory.h>
40 #include <pwd.h>
41 #include <grp.h>
42 #include <door.h>
43 #include <syslog.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <assert.h>
47 #include <deflt.h>
48 #include <nfs/nfs4.h>
49 #include <nfs/nfssys.h>
50 #include <nfs/nfsid_map.h>
51 #include <nfs/mapid.h>
52 #include <sys/sdt.h>
53 #include <sys/idmap.h>
54 #include <idmap.h>
55 #include <sys/fs/autofs.h>
56 #include <sys/mkdev.h>
57 #include "nfs_resolve.h"
58
59 #define UID_MAX_STR_LEN 11 /* Digits in UID_MAX + 1 */
60 #define DIAG_FILE "/var/run/nfs4_domain"
61
62 /*
63 * idmap_kcall() takes a door descriptor as it's argument when we
64 * need to (re)establish the in-kernel door handles. When we only
65 * want to flush the id kernel caches, we don't redo the door setup.
66 */
67 #define FLUSH_KCACHES_ONLY (int)-1
68
69 FILE *n4_fp;
70 int n4_fd;
71
72 extern size_t pwd_buflen;
73 extern size_t grp_buflen;
74 extern thread_t sig_thread;
75
76 /*
77 * Prototypes
78 */
79 extern void check_domain(int);
80 extern void idmap_kcall(int);
81 extern int _nfssys(int, void *);
82 extern int valid_domain(const char *);
83 extern int validate_id_str(const char *);
84 extern int extract_domain(char *, char **, char **);
85 extern void update_diag_file(char *);
86 extern void *cb_update_domain(void *);
87 extern int cur_domain_null(void);
88
89 void
nfsmapid_str_uid(struct mapid_arg * argp,size_t arg_size)90 nfsmapid_str_uid(struct mapid_arg *argp, size_t arg_size)
91 {
92 struct mapid_res result;
93 struct passwd pwd;
94 struct passwd *pwd_ptr;
95 int pwd_rc;
96 char *pwd_buf;
97 char *user;
98 char *domain;
99 idmap_stat rc;
100
101 if (argp->u_arg.len <= 0 || arg_size < MAPID_ARG_LEN(argp->u_arg.len)) {
102 result.status = NFSMAPID_INVALID;
103 result.u_res.uid = UID_NOBODY;
104 goto done;
105 }
106
107 if (!extract_domain(argp->str, &user, &domain)) {
108 unsigned long id;
109
110 /*
111 * Invalid "user@domain" string. Still, the user
112 * part might be an encoded uid, so do a final check.
113 * Remember, domain part of string was not set since
114 * not a valid string.
115 */
116 if (!validate_id_str(user)) {
117 result.status = NFSMAPID_UNMAPPABLE;
118 result.u_res.uid = UID_NOBODY;
119 goto done;
120 }
121
122 errno = 0;
123 id = strtoul(user, (char **)NULL, 10);
124
125 /*
126 * We don't accept ephemeral ids from the wire.
127 */
128 if (errno || id > UID_MAX) {
129 result.status = NFSMAPID_UNMAPPABLE;
130 result.u_res.uid = UID_NOBODY;
131 goto done;
132 }
133
134 result.u_res.uid = (uid_t)id;
135 result.status = NFSMAPID_NUMSTR;
136 goto done;
137 }
138
139 /*
140 * String properly constructed. Now we check for domain and
141 * group validity.
142 */
143 if (!cur_domain_null() && !valid_domain(domain)) {
144 /*
145 * If the domain part of the string does not
146 * match the NFS domain, try to map it using
147 * idmap service.
148 */
149 rc = idmap_getuidbywinname(user, domain, 0, &result.u_res.uid);
150 if (rc != IDMAP_SUCCESS) {
151 result.status = NFSMAPID_BADDOMAIN;
152 result.u_res.uid = UID_NOBODY;
153 goto done;
154 }
155 result.status = NFSMAPID_OK;
156 goto done;
157 }
158
159 if ((pwd_buf = malloc(pwd_buflen)) == NULL ||
160 (pwd_rc = getpwnam_r(user, &pwd, pwd_buf, pwd_buflen, &pwd_ptr))
161 != 0 || pwd_ptr == NULL) {
162
163 if (pwd_buf == NULL || pwd_rc != 0)
164 result.status = NFSMAPID_INTERNAL;
165 else {
166 /*
167 * Not a valid user
168 */
169 result.status = NFSMAPID_NOTFOUND;
170 free(pwd_buf);
171 }
172 result.u_res.uid = UID_NOBODY;
173 goto done;
174 }
175
176 /*
177 * Valid user entry
178 */
179 result.u_res.uid = pwd.pw_uid;
180 result.status = NFSMAPID_OK;
181 free(pwd_buf);
182 done:
183 (void) door_return((char *)&result, sizeof (struct mapid_res), NULL, 0);
184 }
185
186 /* ARGSUSED1 */
187 void
nfsmapid_uid_str(struct mapid_arg * argp,size_t arg_size)188 nfsmapid_uid_str(struct mapid_arg *argp, size_t arg_size)
189 {
190 struct mapid_res result;
191 struct mapid_res *resp;
192 struct passwd pwd;
193 struct passwd *pwd_ptr;
194 char *pwd_buf = NULL;
195 char *idmap_buf = NULL;
196 uid_t uid = argp->u_arg.uid;
197 size_t uid_str_len;
198 char *pw_str;
199 size_t pw_str_len;
200 char *at_str;
201 size_t at_str_len;
202 char dom_str[DNAMEMAX];
203 size_t dom_str_len;
204 idmap_stat rc;
205
206 if (uid == (uid_t)-1) {
207 /*
208 * Sentinel uid is not a valid id
209 */
210 resp = &result;
211 resp->status = NFSMAPID_BADID;
212 resp->u_res.len = 0;
213 goto done;
214 }
215
216 /*
217 * Make local copy of domain for further manipuation
218 * NOTE: mapid_get_domain() returns a ptr to TSD.
219 */
220 if (cur_domain_null()) {
221 dom_str_len = 0;
222 dom_str[0] = '\0';
223 } else {
224 dom_str_len = strlcpy(dom_str, mapid_get_domain(), DNAMEMAX);
225 }
226
227 /*
228 * If uid is ephemeral then resolve it using idmap service
229 */
230 if (uid > UID_MAX) {
231 rc = idmap_getwinnamebyuid(uid, 0, &idmap_buf, NULL);
232 if (rc != IDMAP_SUCCESS) {
233 /*
234 * We don't put stringified ephemeral uids on
235 * the wire.
236 */
237 resp = &result;
238 resp->status = NFSMAPID_UNMAPPABLE;
239 resp->u_res.len = 0;
240 goto done;
241 }
242
243 /*
244 * idmap_buf is already in the desired form i.e. name@domain
245 */
246 pw_str = idmap_buf;
247 pw_str_len = strlen(pw_str);
248 at_str_len = dom_str_len = 0;
249 at_str = "";
250 dom_str[0] = '\0';
251 goto gen_result;
252 }
253
254 /*
255 * Handling non-ephemeral uids
256 *
257 * We want to encode the uid into a literal string... :
258 *
259 * - upon failure to allocate space from the heap
260 * - if there is no current domain configured
261 * - if there is no such uid in the passwd DB's
262 */
263 if ((pwd_buf = malloc(pwd_buflen)) == NULL || dom_str_len == 0 ||
264 getpwuid_r(uid, &pwd, pwd_buf, pwd_buflen, &pwd_ptr) != 0 ||
265 pwd_ptr == NULL) {
266
267 /*
268 * If we could not allocate from the heap, try
269 * allocating from the stack as a last resort.
270 */
271 if (pwd_buf == NULL && (pwd_buf =
272 alloca(MAPID_RES_LEN(UID_MAX_STR_LEN))) == NULL) {
273 resp = &result;
274 resp->status = NFSMAPID_INTERNAL;
275 resp->u_res.len = 0;
276 goto done;
277 }
278
279 /*
280 * Constructing literal string without '@' so that
281 * we'll know that it's not a user, but rather a
282 * uid encoded string.
283 */
284 pw_str = pwd_buf;
285 (void) sprintf(pw_str, "%u", uid);
286 pw_str_len = strlen(pw_str);
287 at_str_len = dom_str_len = 0;
288 at_str = "";
289 dom_str[0] = '\0';
290 } else {
291 /*
292 * Otherwise, we construct the "user@domain" string if
293 * it's not already in that form.
294 */
295 pw_str = pwd.pw_name;
296 pw_str_len = strlen(pw_str);
297 if (strchr(pw_str, '@') == NULL) {
298 at_str = "@";
299 at_str_len = 1;
300 } else {
301 at_str_len = dom_str_len = 0;
302 at_str = "";
303 dom_str[0] = '\0';
304 }
305 }
306
307 gen_result:
308 uid_str_len = pw_str_len + at_str_len + dom_str_len;
309 if ((resp = alloca(MAPID_RES_LEN(uid_str_len))) == NULL) {
310 resp = &result;
311 resp->status = NFSMAPID_INTERNAL;
312 resp->u_res.len = 0;
313 goto done;
314 }
315 /* LINTED format argument to sprintf */
316 (void) sprintf(resp->str, "%s%s%s", pw_str, at_str, dom_str);
317 resp->u_res.len = uid_str_len;
318 if (pwd_buf)
319 free(pwd_buf);
320 if (idmap_buf)
321 idmap_free(idmap_buf);
322 resp->status = NFSMAPID_OK;
323
324 done:
325 /*
326 * There is a chance that the door_return will fail because the
327 * resulting string is too large, try to indicate that if possible
328 */
329 if (door_return((char *)resp,
330 MAPID_RES_LEN(resp->u_res.len), NULL, 0) == -1) {
331 resp->status = NFSMAPID_INTERNAL;
332 resp->u_res.len = 0;
333 (void) door_return((char *)&result, sizeof (struct mapid_res),
334 NULL, 0);
335 }
336 }
337
338 void
nfsmapid_str_gid(struct mapid_arg * argp,size_t arg_size)339 nfsmapid_str_gid(struct mapid_arg *argp, size_t arg_size)
340 {
341 struct mapid_res result;
342 struct group grp;
343 struct group *grp_ptr;
344 int grp_rc;
345 char *grp_buf;
346 char *group;
347 char *domain;
348 idmap_stat rc;
349
350 if (argp->u_arg.len <= 0 ||
351 arg_size < MAPID_ARG_LEN(argp->u_arg.len)) {
352 result.status = NFSMAPID_INVALID;
353 result.u_res.gid = GID_NOBODY;
354 goto done;
355 }
356
357 if (!extract_domain(argp->str, &group, &domain)) {
358 unsigned long id;
359
360 /*
361 * Invalid "group@domain" string. Still, the
362 * group part might be an encoded gid, so do a
363 * final check. Remember, domain part of string
364 * was not set since not a valid string.
365 */
366 if (!validate_id_str(group)) {
367 result.status = NFSMAPID_UNMAPPABLE;
368 result.u_res.gid = GID_NOBODY;
369 goto done;
370 }
371
372 errno = 0;
373 id = strtoul(group, (char **)NULL, 10);
374
375 /*
376 * We don't accept ephemeral ids from the wire.
377 */
378 if (errno || id > UID_MAX) {
379 result.status = NFSMAPID_UNMAPPABLE;
380 result.u_res.gid = GID_NOBODY;
381 goto done;
382 }
383
384 result.u_res.gid = (gid_t)id;
385 result.status = NFSMAPID_NUMSTR;
386 goto done;
387 }
388
389 /*
390 * String properly constructed. Now we check for domain and
391 * group validity.
392 */
393 if (!cur_domain_null() && !valid_domain(domain)) {
394 /*
395 * If the domain part of the string does not
396 * match the NFS domain, try to map it using
397 * idmap service.
398 */
399 rc = idmap_getgidbywinname(group, domain, 0, &result.u_res.gid);
400 if (rc != IDMAP_SUCCESS) {
401 result.status = NFSMAPID_BADDOMAIN;
402 result.u_res.gid = GID_NOBODY;
403 goto done;
404 }
405 result.status = NFSMAPID_OK;
406 goto done;
407 }
408
409 if ((grp_buf = malloc(grp_buflen)) == NULL ||
410 (grp_rc = getgrnam_r(group, &grp, grp_buf, grp_buflen, &grp_ptr))
411 != 0 || grp_ptr == NULL) {
412
413 if (grp_buf == NULL || grp_rc != 0)
414 result.status = NFSMAPID_INTERNAL;
415 else {
416 /*
417 * Not a valid group
418 */
419 result.status = NFSMAPID_NOTFOUND;
420 free(grp_buf);
421 }
422 result.u_res.gid = GID_NOBODY;
423 goto done;
424 }
425
426 /*
427 * Valid group entry
428 */
429 result.status = NFSMAPID_OK;
430 result.u_res.gid = grp.gr_gid;
431 free(grp_buf);
432 done:
433 (void) door_return((char *)&result, sizeof (struct mapid_res), NULL, 0);
434 }
435
436 /* ARGSUSED1 */
437 void
nfsmapid_gid_str(struct mapid_arg * argp,size_t arg_size)438 nfsmapid_gid_str(struct mapid_arg *argp, size_t arg_size)
439 {
440 struct mapid_res result;
441 struct mapid_res *resp;
442 struct group grp;
443 struct group *grp_ptr;
444 char *grp_buf = NULL;
445 char *idmap_buf = NULL;
446 idmap_stat rc;
447 gid_t gid = argp->u_arg.gid;
448 size_t gid_str_len;
449 char *gr_str;
450 size_t gr_str_len;
451 char *at_str;
452 size_t at_str_len;
453 char dom_str[DNAMEMAX];
454 size_t dom_str_len;
455
456 if (gid == (gid_t)-1) {
457 /*
458 * Sentinel gid is not a valid id
459 */
460 resp = &result;
461 resp->status = NFSMAPID_BADID;
462 resp->u_res.len = 0;
463 goto done;
464 }
465
466 /*
467 * Make local copy of domain for further manipuation
468 * NOTE: mapid_get_domain() returns a ptr to TSD.
469 */
470 if (cur_domain_null()) {
471 dom_str_len = 0;
472 dom_str[0] = '\0';
473 } else {
474 dom_str_len = strlen(mapid_get_domain());
475 bcopy(mapid_get_domain(), dom_str, dom_str_len);
476 dom_str[dom_str_len] = '\0';
477 }
478
479 /*
480 * If gid is ephemeral then resolve it using idmap service
481 */
482 if (gid > UID_MAX) {
483 rc = idmap_getwinnamebygid(gid, 0, &idmap_buf, NULL);
484 if (rc != IDMAP_SUCCESS) {
485 /*
486 * We don't put stringified ephemeral gids on
487 * the wire.
488 */
489 resp = &result;
490 resp->status = NFSMAPID_UNMAPPABLE;
491 resp->u_res.len = 0;
492 goto done;
493 }
494
495 /*
496 * idmap_buf is already in the desired form i.e. name@domain
497 */
498 gr_str = idmap_buf;
499 gr_str_len = strlen(gr_str);
500 at_str_len = dom_str_len = 0;
501 at_str = "";
502 dom_str[0] = '\0';
503 goto gen_result;
504 }
505
506 /*
507 * Handling non-ephemeral gids
508 *
509 * We want to encode the gid into a literal string... :
510 *
511 * - upon failure to allocate space from the heap
512 * - if there is no current domain configured
513 * - if there is no such gid in the group DB's
514 */
515 if ((grp_buf = malloc(grp_buflen)) == NULL || dom_str_len == 0 ||
516 getgrgid_r(gid, &grp, grp_buf, grp_buflen, &grp_ptr) != 0 ||
517 grp_ptr == NULL) {
518
519 /*
520 * If we could not allocate from the heap, try
521 * allocating from the stack as a last resort.
522 */
523 if (grp_buf == NULL && (grp_buf =
524 alloca(MAPID_RES_LEN(UID_MAX_STR_LEN))) == NULL) {
525 resp = &result;
526 resp->status = NFSMAPID_INTERNAL;
527 resp->u_res.len = 0;
528 goto done;
529 }
530
531 /*
532 * Constructing literal string without '@' so that
533 * we'll know that it's not a group, but rather a
534 * gid encoded string.
535 */
536 gr_str = grp_buf;
537 (void) sprintf(gr_str, "%u", gid);
538 gr_str_len = strlen(gr_str);
539 at_str_len = dom_str_len = 0;
540 at_str = "";
541 dom_str[0] = '\0';
542 } else {
543 /*
544 * Otherwise, we construct the "group@domain" string if
545 * it's not already in that form.
546 */
547 gr_str = grp.gr_name;
548 gr_str_len = strlen(gr_str);
549 if (strchr(gr_str, '@') == NULL) {
550 at_str = "@";
551 at_str_len = 1;
552 } else {
553 at_str_len = dom_str_len = 0;
554 at_str = "";
555 dom_str[0] = '\0';
556 }
557 }
558
559 gen_result:
560 gid_str_len = gr_str_len + at_str_len + dom_str_len;
561 if ((resp = alloca(MAPID_RES_LEN(gid_str_len))) == NULL) {
562 resp = &result;
563 resp->status = NFSMAPID_INTERNAL;
564 resp->u_res.len = 0;
565 goto done;
566 }
567 /* LINTED format argument to sprintf */
568 (void) sprintf(resp->str, "%s%s%s", gr_str, at_str, dom_str);
569 resp->u_res.len = gid_str_len;
570 if (grp_buf)
571 free(grp_buf);
572 if (idmap_buf)
573 idmap_free(idmap_buf);
574 resp->status = NFSMAPID_OK;
575
576 done:
577 /*
578 * There is a chance that the door_return will fail because the
579 * resulting string is too large, try to indicate that if possible
580 */
581 if (door_return((char *)resp,
582 MAPID_RES_LEN(resp->u_res.len), NULL, 0) == -1) {
583 resp->status = NFSMAPID_INTERNAL;
584 resp->u_res.len = 0;
585 (void) door_return((char *)&result, sizeof (struct mapid_res),
586 NULL, 0);
587 }
588 }
589
590 void
nfsmapid_server_netinfo(refd_door_args_t * referral_args,size_t arg_size)591 nfsmapid_server_netinfo(refd_door_args_t *referral_args, size_t arg_size)
592 {
593 char *res;
594 int res_size;
595 int error;
596 int srsz = 0;
597 char host[MAXHOSTNAMELEN];
598 utf8string *nfsfsloc_args;
599 refd_door_res_t *door_res;
600 refd_door_res_t failed_res;
601 struct nfs_fsl_info *nfs_fsloc_res;
602
603 if (arg_size < sizeof (refd_door_args_t)) {
604 failed_res.res_status = EINVAL;
605 res = (char *)&failed_res;
606 res_size = sizeof (refd_door_res_t);
607 syslog(LOG_ERR,
608 "nfsmapid_server_netinfo failed: Invalid data\n");
609 goto send_response;
610 }
611
612 if (decode_args(xdr_utf8string, (refd_door_args_t *)referral_args,
613 (caddr_t *)&nfsfsloc_args, sizeof (utf8string))) {
614 syslog(LOG_ERR, "cannot allocate memory");
615 failed_res.res_status = ENOMEM;
616 failed_res.xdr_len = 0;
617 res = (caddr_t)&failed_res;
618 res_size = sizeof (refd_door_res_t);
619 goto send_response;
620 }
621
622 if (nfsfsloc_args->utf8string_len >= MAXHOSTNAMELEN) {
623 syslog(LOG_ERR, "argument too large");
624 failed_res.res_status = EOVERFLOW;
625 failed_res.xdr_len = 0;
626 res = (caddr_t)&failed_res;
627 res_size = sizeof (refd_door_res_t);
628 goto send_response;
629 }
630
631 snprintf(host, nfsfsloc_args->utf8string_len + 1,
632 "%s", nfsfsloc_args->utf8string_val);
633
634 nfs_fsloc_res =
635 get_nfs4ref_info(host, NFS_PORT, NFS_V4);
636
637 xdr_free(xdr_utf8string, (char *)&nfsfsloc_args);
638
639 if (nfs_fsloc_res) {
640 error = 0;
641 error = encode_res(xdr_nfs_fsl_info, &door_res,
642 (caddr_t)nfs_fsloc_res, &res_size);
643 free_nfs4ref_info(nfs_fsloc_res);
644 if (error != 0) {
645 syslog(LOG_ERR,
646 "error allocating fs_locations "
647 "results buffer");
648 failed_res.res_status = error;
649 failed_res.xdr_len = srsz;
650 res = (caddr_t)&failed_res;
651 res_size = sizeof (refd_door_res_t);
652 } else {
653 door_res->res_status = 0;
654 res = (caddr_t)door_res;
655 }
656 } else {
657 failed_res.res_status = EINVAL;
658 failed_res.xdr_len = 0;
659 res = (caddr_t)&failed_res;
660 res_size = sizeof (refd_door_res_t);
661 }
662
663 send_response:
664 srsz = res_size;
665 errno = 0;
666
667 error = door_return(res, res_size, NULL, 0);
668 if (errno == E2BIG) {
669 failed_res.res_status = EOVERFLOW;
670 failed_res.xdr_len = srsz;
671 res = (caddr_t)&failed_res;
672 res_size = sizeof (refd_door_res_t);
673 } else {
674 res = NULL;
675 res_size = 0;
676 }
677
678 door_return(res, res_size, NULL, 0);
679 }
680
681 /* ARGSUSED */
682 void
nfsmapid_func(void * cookie,char * argp,size_t arg_size,door_desc_t * dp,uint_t n_desc)683 nfsmapid_func(void *cookie, char *argp, size_t arg_size,
684 door_desc_t *dp, uint_t n_desc)
685 {
686 struct mapid_arg *mapargp;
687 struct mapid_res mapres;
688 refd_door_args_t *referral_args;
689
690 /*
691 * Make sure we have a valid argument
692 */
693 if (arg_size < sizeof (struct mapid_arg)) {
694 mapres.status = NFSMAPID_INVALID;
695 mapres.u_res.len = 0;
696 (void) door_return((char *)&mapres, sizeof (struct mapid_res),
697 NULL, 0);
698 return;
699 }
700
701 /* LINTED pointer cast */
702 mapargp = (struct mapid_arg *)argp;
703 referral_args = (refd_door_args_t *)argp;
704 switch (mapargp->cmd) {
705 case NFSMAPID_STR_UID:
706 nfsmapid_str_uid(mapargp, arg_size);
707 return;
708 case NFSMAPID_UID_STR:
709 nfsmapid_uid_str(mapargp, arg_size);
710 return;
711 case NFSMAPID_STR_GID:
712 nfsmapid_str_gid(mapargp, arg_size);
713 return;
714 case NFSMAPID_GID_STR:
715 nfsmapid_gid_str(mapargp, arg_size);
716 return;
717 case NFSMAPID_SRV_NETINFO:
718 nfsmapid_server_netinfo(referral_args, arg_size);
719 default:
720 break;
721 }
722 mapres.status = NFSMAPID_INVALID;
723 mapres.u_res.len = 0;
724 (void) door_return((char *)&mapres, sizeof (struct mapid_res), NULL, 0);
725 }
726
727 /*
728 * mapid_get_domain() always returns a ptr to TSD, so the
729 * check for a NULL domain is not a simple comparison with
730 * NULL but we need to check the contents of the TSD data.
731 */
732 int
cur_domain_null(void)733 cur_domain_null(void)
734 {
735 char *p;
736
737 if ((p = mapid_get_domain()) == NULL)
738 return (1);
739
740 return (p[0] == '\0');
741 }
742
743 int
extract_domain(char * cp,char ** upp,char ** dpp)744 extract_domain(char *cp, char **upp, char **dpp)
745 {
746 /*
747 * Caller must insure that the string is valid
748 */
749 *upp = cp;
750
751 if ((*dpp = strchr(cp, '@')) == NULL)
752 return (0);
753 *(*dpp)++ = '\0';
754 return (1);
755 }
756
757 int
valid_domain(const char * dom)758 valid_domain(const char *dom)
759 {
760 const char *whoami = "valid_domain";
761
762 if (!mapid_stdchk_domain(dom)) {
763 syslog(LOG_ERR, gettext("%s: Invalid inbound domain name %s."),
764 whoami, dom);
765 return (0);
766 }
767
768 /*
769 * NOTE: mapid_get_domain() returns a ptr to TSD.
770 */
771 return (strcasecmp(dom, mapid_get_domain()) == 0);
772 }
773
774 int
validate_id_str(const char * id)775 validate_id_str(const char *id)
776 {
777 while (*id) {
778 if (!isdigit(*id++))
779 return (0);
780 }
781 return (1);
782 }
783
784 void
idmap_kcall(int door_id)785 idmap_kcall(int door_id)
786 {
787 struct nfsidmap_args args;
788
789 if (door_id >= 0) {
790 args.state = 1;
791 args.did = door_id;
792 } else {
793 args.state = 0;
794 args.did = 0;
795 }
796 (void) _nfssys(NFS_IDMAP, &args);
797 }
798
799 /*
800 * Get the current NFS domain.
801 *
802 * If nfsmapid_domain is set in NFS SMF, then it is the NFS domain;
803 * otherwise, the DNS domain is used.
804 */
805 void
check_domain(int sighup)806 check_domain(int sighup)
807 {
808 const char *whoami = "check_domain";
809 static int setup_done = 0;
810 static cb_t cb;
811
812 /*
813 * Construct the arguments to be passed to libmapid interface
814 * If called in response to a SIGHUP, reset any cached DNS TXT
815 * RR state.
816 */
817 cb.fcn = cb_update_domain;
818 cb.signal = sighup;
819 mapid_reeval_domain(&cb);
820
821 /*
822 * Restart the signal handler thread if we're still setting up
823 */
824 if (!setup_done) {
825 setup_done = 1;
826 if (thr_continue(sig_thread)) {
827 syslog(LOG_ERR, gettext("%s: Fatal error: signal "
828 "handler thread could not be restarted."), whoami);
829 exit(6);
830 }
831 }
832 }
833
834 /*
835 * Need to be able to open the DIAG_FILE before nfsmapid(1m)
836 * releases it's root priviledges. The DIAG_FILE then remains
837 * open for the duration of this nfsmapid instance via n4_fd.
838 */
839 void
open_diag_file()840 open_diag_file()
841 {
842 static int msg_done = 0;
843
844 if ((n4_fp = fopen(DIAG_FILE, "w+")) != NULL) {
845 n4_fd = fileno(n4_fp);
846 return;
847 }
848
849 if (msg_done)
850 return;
851
852 syslog(LOG_ERR, "Failed to create %s. Enable syslog "
853 "daemon.debug for more info", DIAG_FILE);
854 msg_done = 1;
855 }
856
857 /*
858 * When a new domain name is configured, save to DIAG_FILE
859 * and log to syslog, with LOG_DEBUG level (if configured).
860 */
861 void
update_diag_file(char * new)862 update_diag_file(char *new)
863 {
864 char buf[DNAMEMAX];
865 ssize_t n;
866 size_t len;
867
868 (void) lseek(n4_fd, (off_t)0, SEEK_SET);
869 (void) ftruncate(n4_fd, 0);
870 (void) snprintf(buf, DNAMEMAX, "%s\n", new);
871
872 len = strlen(buf);
873 n = write(n4_fd, buf, len);
874 if (n < 0 || n < len)
875 syslog(LOG_DEBUG, "Could not write %s to diag file", new);
876 (void) fsync(n4_fd);
877
878 syslog(LOG_DEBUG, "nfsmapid domain = %s", new);
879 }
880
881 /*
882 * Callback function for libmapid. This will be called
883 * by the lib, everytime the nfsmapid(1m) domain changes.
884 */
885 void *
cb_update_domain(void * arg)886 cb_update_domain(void *arg)
887 {
888 char *new_dname = (char *)arg;
889
890 DTRACE_PROBE1(nfsmapid, daemon__domain, new_dname);
891 update_diag_file(new_dname);
892 idmap_kcall(FLUSH_KCACHES_ONLY);
893
894 return (NULL);
895 }
896
897 bool_t
xdr_utf8string(XDR * xdrs,utf8string * objp)898 xdr_utf8string(XDR *xdrs, utf8string *objp)
899 {
900 if (xdrs->x_op != XDR_FREE)
901 return (xdr_bytes(xdrs, (char **)&objp->utf8string_val,
902 (uint_t *)&objp->utf8string_len, NFS4_MAX_UTF8STRING));
903 return (TRUE);
904 }
905
906
907 int
decode_args(xdrproc_t xdrfunc,refd_door_args_t * argp,caddr_t * xdrargs,int size)908 decode_args(xdrproc_t xdrfunc, refd_door_args_t *argp, caddr_t *xdrargs,
909 int size)
910 {
911 XDR xdrs;
912
913 caddr_t tmpargs = (caddr_t)&((refd_door_args_t *)argp)->xdr_arg;
914 size_t arg_size = ((refd_door_args_t *)argp)->xdr_len;
915
916 xdrmem_create(&xdrs, tmpargs, arg_size, XDR_DECODE);
917
918 *xdrargs = calloc(1, size);
919 if (*xdrargs == NULL) {
920 syslog(LOG_ERR, "error allocating arguments buffer");
921 return (ENOMEM);
922 }
923
924 if (!(*xdrfunc)(&xdrs, *xdrargs)) {
925 free(*xdrargs);
926 *xdrargs = NULL;
927 syslog(LOG_ERR, "error decoding arguments");
928 return (EINVAL);
929 }
930
931 return (0);
932 }
933
934 int
encode_res(xdrproc_t xdrfunc,refd_door_res_t ** results,caddr_t resp,int * size)935 encode_res(
936 xdrproc_t xdrfunc,
937 refd_door_res_t **results,
938 caddr_t resp,
939 int *size)
940 {
941 XDR xdrs;
942
943 *size = xdr_sizeof((*xdrfunc), resp);
944 *results = malloc(sizeof (refd_door_res_t) + *size);
945 if (*results == NULL) {
946 return (ENOMEM);
947 }
948 (*results)->xdr_len = *size;
949 *size = sizeof (refd_door_res_t) + (*results)->xdr_len;
950 xdrmem_create(&xdrs, (caddr_t)((*results)->xdr_res),
951 (*results)->xdr_len, XDR_ENCODE);
952 if (!(*xdrfunc)(&xdrs, resp)) {
953 (*results)->res_status = EINVAL;
954 syslog(LOG_ERR, "error encoding results");
955 return ((*results)->res_status);
956 }
957 (*results)->res_status = 0;
958 return ((*results)->res_status);
959 }
960
961
962 bool_t
xdr_knetconfig(XDR * xdrs,struct knetconfig * objp)963 xdr_knetconfig(XDR *xdrs, struct knetconfig *objp)
964 {
965 rpc_inline_t *buf;
966 int i;
967 u_longlong_t dev64;
968 #if !defined(_LP64)
969 uint32_t major, minor;
970 #endif
971
972 if (!xdr_u_int(xdrs, &objp->knc_semantics))
973 return (FALSE);
974 if (!xdr_opaque(xdrs, objp->knc_protofmly, KNC_STRSIZE))
975 return (FALSE);
976 if (!xdr_opaque(xdrs, objp->knc_proto, KNC_STRSIZE))
977 return (FALSE);
978
979 /*
980 * For interoperability between 32-bit daemon and 64-bit kernel,
981 * we always treat dev_t as 64-bit number and do the expanding
982 * or compression of dev_t as needed.
983 * We have to hand craft the conversion since there is no available
984 * function in ddi.c. Besides ddi.c is available only in the kernel
985 * and we want to keep both user and kernel of xdr_knetconfig() the
986 * same for consistency.
987 */
988
989 if (xdrs->x_op == XDR_ENCODE) {
990 #if defined(_LP64)
991 dev64 = objp->knc_rdev;
992 #else
993 major = (objp->knc_rdev >> NBITSMINOR32) & MAXMAJ32;
994 minor = objp->knc_rdev & MAXMIN32;
995 dev64 = (((unsigned long long)major) << NBITSMINOR64) | minor;
996 #endif
997 if (!xdr_u_longlong_t(xdrs, &dev64))
998 return (FALSE);
999 }
1000 if (xdrs->x_op == XDR_DECODE) {
1001 #if defined(_LP64)
1002 if (!xdr_u_longlong_t(xdrs, (u_longlong_t *)&objp->knc_rdev))
1003 return (FALSE);
1004 #else
1005 if (!xdr_u_longlong_t(xdrs, &dev64))
1006 return (FALSE);
1007
1008 major = (dev64 >> NBITSMINOR64) & L_MAXMAJ32;
1009 minor = dev64 & L_MAXMIN32;
1010 objp->knc_rdev = (major << L_BITSMINOR32) | minor;
1011 #endif
1012 }
1013
1014 if (xdrs->x_op == XDR_ENCODE) {
1015 buf = XDR_INLINE(xdrs, (8) * BYTES_PER_XDR_UNIT);
1016 if (buf == NULL) {
1017 if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8,
1018 sizeof (uint_t), (xdrproc_t)xdr_u_int))
1019 return (FALSE);
1020 } else {
1021 uint_t *genp;
1022
1023 for (i = 0, genp = objp->knc_unused;
1024 i < 8; i++) {
1025 #if defined(_LP64) || defined(_KERNEL)
1026 IXDR_PUT_U_INT32(buf, *genp++);
1027 #else
1028 IXDR_PUT_U_LONG(buf, *genp++);
1029 #endif
1030 }
1031 }
1032 return (TRUE);
1033 } else if (xdrs->x_op == XDR_DECODE) {
1034 buf = XDR_INLINE(xdrs, (8) * BYTES_PER_XDR_UNIT);
1035 if (buf == NULL) {
1036 if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8,
1037 sizeof (uint_t), (xdrproc_t)xdr_u_int))
1038 return (FALSE);
1039 } else {
1040 uint_t *genp;
1041
1042 for (i = 0, genp = objp->knc_unused;
1043 i < 8; i++) {
1044 #if defined(_LP64) || defined(_KERNEL)
1045 *genp++ = IXDR_GET_U_INT32(buf);
1046 #else
1047 *genp++ = IXDR_GET_U_LONG(buf);
1048 #endif
1049 }
1050 }
1051 return (TRUE);
1052 }
1053
1054 if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8,
1055 sizeof (uint_t), (xdrproc_t)xdr_u_int))
1056 return (FALSE);
1057 return (TRUE);
1058 }
1059
1060 /*
1061 * used by NFSv4 referrals to get info needed for NFSv4 referral mount.
1062 */
1063 bool_t
xdr_nfs_fsl_info(XDR * xdrs,struct nfs_fsl_info * objp)1064 xdr_nfs_fsl_info(XDR *xdrs, struct nfs_fsl_info *objp)
1065 {
1066
1067 if (!xdr_u_int(xdrs, &objp->netbuf_len))
1068 return (FALSE);
1069 if (!xdr_u_int(xdrs, &objp->netnm_len))
1070 return (FALSE);
1071 if (!xdr_u_int(xdrs, &objp->knconf_len))
1072 return (FALSE);
1073 if (!xdr_string(xdrs, &objp->netname, ~0))
1074 return (FALSE);
1075 if (!xdr_pointer(xdrs, (char **)&objp->addr, objp->netbuf_len,
1076 (xdrproc_t)xdr_netbuf))
1077 return (FALSE);
1078 if (!xdr_pointer(xdrs, (char **)&objp->knconf,
1079 objp->knconf_len, (xdrproc_t)xdr_knetconfig))
1080 return (FALSE);
1081 return (TRUE);
1082 }
1083