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 /* LINTLIBRARY */
22
23 /*
24 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
25 */
26
27 /*
28 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
29 * Use is subject to license terms.
30 */
31
32 /*
33 * nfs security related library routines.
34 *
35 * Some of the routines in this file are adopted from
36 * lib/libnsl/netselect/netselect.c and are modified to be
37 * used for accessing /etc/nfssec.conf.
38 */
39
40 /* SVr4.0 1.18 */
41
42 #include <stdio.h>
43 #include <string.h>
44 #include <ctype.h>
45 #include <stdlib.h>
46 #include <syslog.h>
47 #include <synch.h>
48 #include <rpc/rpc.h>
49 #include <nfs/nfs_sec.h>
50 #include <rpc/rpcsec_gss.h>
51 #ifdef WNFS_SEC_NEGO
52 #include "webnfs.h"
53 #endif
54
55 #define GETBYNAME 1
56 #define GETBYNUM 2
57
58 /*
59 * mapping for /etc/nfssec.conf
60 */
61 struct sc_data {
62 char *string;
63 int value;
64 };
65
66 static struct sc_data sc_service[] = {
67 "default", rpc_gss_svc_default,
68 "-", rpc_gss_svc_none,
69 "none", rpc_gss_svc_none,
70 "integrity", rpc_gss_svc_integrity,
71 "privacy", rpc_gss_svc_privacy,
72 NULL, SC_FAILURE
73 };
74
75 static mutex_t matching_lock = DEFAULTMUTEX;
76 static char *gettoken(char *, int);
77 extern int atoi(const char *str);
78
79 extern bool_t rpc_gss_get_principal_name(rpc_gss_principal_t *, char *,
80 char *, char *, char *);
81
82 extern bool_t rpc_gss_mech_to_oid(char *, rpc_gss_OID *);
83 extern bool_t rpc_gss_qop_to_num(char *, char *, uint_t *);
84
85 /*
86 * blank() returns true if the line is a blank line, 0 otherwise
87 */
88 static int
blank(cp)89 blank(cp)
90 char *cp;
91 {
92 while (*cp && isspace(*cp)) {
93 cp++;
94 }
95 return (*cp == '\0');
96 }
97
98 /*
99 * comment() returns true if the line is a comment, 0 otherwise.
100 */
101 static int
comment(cp)102 comment(cp)
103 char *cp;
104 {
105 while (*cp && isspace(*cp)) {
106 cp++;
107 }
108 return (*cp == '#');
109 }
110
111
112 /*
113 * getvalue() searches for the given string in the given array,
114 * and returns the integer value associated with the string.
115 */
116 static unsigned long
getvalue(cp,sc_data)117 getvalue(cp, sc_data)
118 char *cp;
119 struct sc_data sc_data[];
120 {
121 int i; /* used to index through the given struct sc_data array */
122
123 for (i = 0; sc_data[i].string; i++) {
124 if (strcmp(sc_data[i].string, cp) == 0) {
125 break;
126 }
127 }
128 return (sc_data[i].value);
129 }
130
131 /*
132 * shift1left() moves all characters in the string over 1 to
133 * the left.
134 */
135 static void
shift1left(p)136 shift1left(p)
137 char *p;
138 {
139 for (; *p; p++)
140 *p = *(p + 1);
141 }
142
143
144 /*
145 * gettoken() behaves much like strtok(), except that
146 * it knows about escaped space characters (i.e., space characters
147 * preceeded by a '\' are taken literally).
148 *
149 * XXX We should make this MT-hot by making it more like strtok_r().
150 */
151 static char *
gettoken(cp,skip)152 gettoken(cp, skip)
153 char *cp;
154 int skip;
155 {
156 static char *savep; /* the place where we left off */
157 register char *p; /* the beginning of the new token */
158 register char *retp; /* the token to be returned */
159
160
161 /* Determine if first or subsequent call */
162 p = (cp == NULL)? savep: cp;
163
164 /* Return if no tokens remain. */
165 if (p == 0) {
166 return (NULL);
167 }
168
169 while (isspace(*p))
170 p++;
171
172 if (*p == '\0') {
173 return (NULL);
174 }
175
176 /*
177 * Save the location of the token and then skip past it
178 */
179
180 retp = p;
181 while (*p) {
182 if (isspace(*p)) {
183 if (skip == TRUE) {
184 shift1left(p);
185 continue;
186 } else
187 break;
188 }
189
190 /*
191 * Only process the escape of the space separator;
192 * since the token may contain other separators,
193 * let the other routines handle the escape of
194 * specific characters in the token.
195 */
196
197 if (*p == '\\' && *(p + 1) != '\n' && isspace(*(p + 1))) {
198 shift1left(p);
199 }
200 p++;
201 }
202 if (*p == '\0') {
203 savep = 0; /* indicate this is last token */
204 } else {
205 *p = '\0';
206 savep = ++p;
207 }
208 return (retp);
209 }
210
211 /*
212 * matchname() parses a line of the /etc/nfssec.conf file
213 * and match the sc_name with the given name.
214 * If there is a match, it fills the information into the given
215 * pointer of the seconfig_t structure.
216 *
217 * Returns TRUE if a match is found.
218 */
219 static bool_t
matchname(char * line,char * name,seconfig_t * secp)220 matchname(char *line, char *name, seconfig_t *secp)
221 {
222 char *tok1, *tok2; /* holds a token from the line */
223 char *secname, *gss_mech, *gss_qop; /* pointer to a secmode name */
224
225 if ((secname = gettoken(line, FALSE)) == NULL) {
226 /* bad line */
227 return (FALSE);
228 }
229
230 if (strcmp(secname, name) != 0) {
231 return (FALSE);
232 }
233
234 tok1 = tok2 = NULL;
235 if (((tok1 = gettoken(NULL, FALSE)) == NULL) ||
236 ((gss_mech = gettoken(NULL, FALSE)) == NULL) ||
237 ((gss_qop = gettoken(NULL, FALSE)) == NULL) ||
238 ((tok2 = gettoken(NULL, FALSE)) == NULL) ||
239 ((secp->sc_service = getvalue(tok2, sc_service))
240 == SC_FAILURE)) {
241 return (FALSE);
242 }
243 secp->sc_nfsnum = atoi(tok1);
244 (void) strcpy(secp->sc_name, secname);
245 (void) strcpy(secp->sc_gss_mech, gss_mech);
246 secp->sc_gss_mech_type = NULL;
247 if (secp->sc_gss_mech[0] != '-') {
248 if (!rpc_gss_mech_to_oid(gss_mech, &secp->sc_gss_mech_type) ||
249 !rpc_gss_qop_to_num(gss_qop, gss_mech, &secp->sc_qop)) {
250 return (FALSE);
251 }
252 }
253
254 return (TRUE);
255 }
256
257 /*
258 * matchnum() parses a line of the /etc/nfssec.conf file
259 * and match the sc_nfsnum with the given number.
260 * If it is a match, it fills the information in the given pointer
261 * of the seconfig_t structure.
262 *
263 * Returns TRUE if a match is found.
264 */
265 static bool_t
matchnum(char * line,int num,seconfig_t * secp)266 matchnum(char *line, int num, seconfig_t *secp)
267 {
268 char *tok1, *tok2; /* holds a token from the line */
269 char *secname, *gss_mech, *gss_qop; /* pointer to a secmode name */
270
271 if ((secname = gettoken(line, FALSE)) == NULL) {
272 /* bad line */
273 return (FALSE);
274 }
275
276 tok1 = tok2 = NULL;
277 if ((tok1 = gettoken(NULL, FALSE)) == NULL) {
278 /* bad line */
279 return (FALSE);
280 }
281
282 if ((secp->sc_nfsnum = atoi(tok1)) != num) {
283 return (FALSE);
284 }
285
286 if (((gss_mech = gettoken(NULL, FALSE)) == NULL) ||
287 ((gss_qop = gettoken(NULL, FALSE)) == NULL) ||
288 ((tok2 = gettoken(NULL, FALSE)) == NULL) ||
289 ((secp->sc_service = getvalue(tok2, sc_service))
290 == SC_FAILURE)) {
291 return (FALSE);
292 }
293
294 (void) strcpy(secp->sc_name, secname);
295 (void) strcpy(secp->sc_gss_mech, gss_mech);
296 if (secp->sc_gss_mech[0] != '-') {
297 if (!rpc_gss_mech_to_oid(gss_mech, &secp->sc_gss_mech_type) ||
298 !rpc_gss_qop_to_num(gss_qop, gss_mech, &secp->sc_qop)) {
299 return (FALSE);
300 }
301 }
302
303 return (TRUE);
304 }
305
306 /*
307 * Fill in the RPC Protocol security flavor number
308 * into the sc_rpcnum of seconfig_t structure.
309 *
310 * Mainly to map NFS secmod number to RPCSEC_GSS if
311 * a mechanism name is specified.
312 */
313 static void
get_rpcnum(seconfig_t * secp)314 get_rpcnum(seconfig_t *secp)
315 {
316 if (secp->sc_gss_mech[0] != '-') {
317 secp->sc_rpcnum = RPCSEC_GSS;
318 } else {
319 secp->sc_rpcnum = secp->sc_nfsnum;
320 }
321 }
322
323 /*
324 * Parse a given hostname (nodename[.domain@realm]) to
325 * instant name (nodename[.domain]) and realm.
326 *
327 * Assuming user has allocated the space for inst and realm.
328 */
329 static int
parsehostname(char * hostname,char * inst,char * realm)330 parsehostname(char *hostname, char *inst, char *realm)
331 {
332 char *h, *r;
333
334 if (!hostname)
335 return (0);
336
337 h = (char *)strdup(hostname);
338 if (!h) {
339 syslog(LOG_ERR, "parsehostname: no memory\n");
340 return (0);
341 }
342
343 r = (char *)strchr(h, '@');
344 if (!r) {
345 (void) strcpy(inst, h);
346 (void) strcpy(realm, "");
347 } else {
348 *r++ = '\0';
349 (void) strcpy(inst, h);
350 (void) strcpy(realm, r);
351 }
352 free(h);
353 return (1);
354 }
355
356 /*
357 * Get the name corresponding to a qop num.
358 */
359 char *
nfs_get_qop_name(seconfig_t * entryp)360 nfs_get_qop_name(seconfig_t *entryp)
361 {
362 char *tok; /* holds a token from the line */
363 char *secname, *gss_qop = NULL; /* pointer to a secmode name */
364 char line[BUFSIZ]; /* holds each line of NFSSEC_CONF */
365 FILE *fp; /* file stream for NFSSEC_CONF */
366
367 (void) mutex_lock(&matching_lock);
368 if ((fp = fopen(NFSSEC_CONF, "r")) == NULL) {
369 (void) mutex_unlock(&matching_lock);
370 return (NULL);
371 }
372
373 while (fgets(line, BUFSIZ, fp)) {
374 if (!(blank(line) || comment(line))) {
375 if ((secname = gettoken(line, FALSE)) == NULL) {
376 /* bad line */
377 continue;
378 }
379 if (strcmp(secname, entryp->sc_name) == 0) {
380 tok = NULL;
381 if ((tok = gettoken(NULL, FALSE)) == NULL) {
382 /* bad line */
383 goto err;
384 }
385
386 if (atoi(tok) != entryp->sc_nfsnum)
387 goto err;
388
389 if ((gettoken(NULL, FALSE) == NULL) ||
390 ((gss_qop = gettoken(NULL, FALSE))
391 == NULL)) {
392 goto err;
393 }
394 break;
395 }
396 }
397 }
398 err:
399 (void) fclose(fp);
400 (void) mutex_unlock(&matching_lock);
401 return (gss_qop);
402 }
403
404 /*
405 * This routine creates an auth handle assocaited with the
406 * negotiated security flavor contained in nfs_sec. The auth
407 * handle will be used in the next LOOKUP request to fetch
408 * the filehandle.
409 */
410 AUTH *
nfs_create_ah(CLIENT * cl,char * hostname,seconfig_t * nfs_sec)411 nfs_create_ah(CLIENT *cl, char *hostname, seconfig_t *nfs_sec)
412 {
413 char netname[MAXNETNAMELEN+1];
414 char svc_name[MAXNETNAMELEN+1];
415 char *gss_qop;
416 static int window = 60;
417
418 if (nfs_sec == NULL)
419 goto err;
420
421 switch (nfs_sec->sc_rpcnum) {
422 case AUTH_UNIX:
423 case AUTH_NONE:
424 return (NULL);
425
426 case AUTH_DES:
427 if (!host2netname(netname, hostname, NULL))
428 goto err;
429
430 return (authdes_seccreate(netname, window, hostname,
431 NULL));
432
433 case RPCSEC_GSS:
434 if (cl == NULL)
435 goto err;
436
437 if (nfs_sec->sc_gss_mech_type == NULL) {
438 syslog(LOG_ERR,
439 "nfs_create_ah: need mechanism information\n");
440 goto err;
441 }
442
443 /*
444 * RPCSEC_GSS service names are of the form svc@host.dom
445 */
446 (void) sprintf(svc_name, "nfs@%s", hostname);
447
448 gss_qop = nfs_get_qop_name(nfs_sec);
449 if (gss_qop == NULL)
450 goto err;
451
452 return (rpc_gss_seccreate(cl, svc_name,
453 nfs_sec->sc_gss_mech, nfs_sec->sc_service, gss_qop,
454 NULL, NULL));
455
456 default:
457 syslog(LOG_ERR, "nfs_create_ah: unknown flavor\n");
458 return (NULL);
459 }
460 err:
461 syslog(LOG_ERR, "nfs_create_ah: failed to make auth handle\n");
462 return (NULL);
463 }
464
465 #ifdef WNFS_SEC_NEGO
466 /*
467 * This routine negotiates sec flavors with server and returns:
468 * SNEGO_SUCCESS: successful; sec flavors are
469 * returned in snego,
470 * SNEGO_DEF_VALID: default sec flavor valid; no need
471 * to negotiate flavors,
472 * SNEGO_ARRAY_TOO_SMALL: array too small,
473 * SNEGO_FAILURE: failure
474 */
475 /*
476 * The following depicts how sec flavors are placed in an
477 * overloaded V2 fhandle:
478 *
479 * Note that the first four octets contain the length octet,
480 * the status octet, and two padded octets to make them XDR
481 * four-octet aligned.
482 *
483 * 1 2 3 4 32
484 * +---+---+---+---+---+---+---+---+ +---+---+---+---+ +---+
485 * | l | s | | | sec_1 |...| sec_n |...| |
486 * +---+---+---+---+---+---+---+---+ +---+---+---+---+ +---+
487 *
488 * where
489 *
490 * the status octet s indicates whether there are more security
491 * flavors(1 means yes, 0 means no) that require the client to
492 * perform another 0x81 LOOKUP to get them,
493 *
494 * the length octet l is the length describing the number of
495 * valid octets that follow. (l = 4 * n, where n is the number
496 *
497 * The following depicts how sec flavors are placed in an
498 * overloaded V3 fhandle:
499 *
500 * 1 4
501 * +--+--+--+--+
502 * | len |
503 * +--+--+--+--+
504 * up to 64
505 * +--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+
506 * |s | | | | sec_1 | sec_2 | ... | sec_n |
507 * +--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+
508 *
509 * len = 4 * (n+1), where n is the number of security flavors
510 * sent in the current overloaded filehandle.
511 *
512 * the status octet s indicates whether there are more security
513 * mechanisms(1 means yes, 0 means no) that require the client
514 * to perform another 0x81 LOOKUP to get them.
515 *
516 * Three octets are padded after the status octet.
517 */
518 enum snego_stat
nfs_sec_nego(rpcprog_t vers,CLIENT * clnt,char * fspath,struct snego_t * snego)519 nfs_sec_nego(rpcprog_t vers, CLIENT *clnt, char *fspath, struct snego_t *snego)
520 {
521 enum clnt_stat rpc_stat;
522 static int MAX_V2_CNT = (WNL_FHSIZE/sizeof (int)) - 1;
523 static int MAX_V3_CNT = (WNL3_FHSIZE/sizeof (int)) - 1;
524 static struct timeval TIMEOUT = { 25, 0 };
525 int status;
526
527 if (clnt == NULL || fspath == NULL || snego == NULL)
528 return (SNEGO_FAILURE);
529
530 if (vers == WNL_V2) {
531 wnl_diropargs arg;
532 wnl_diropres clnt_res;
533
534 memset((char *)&arg.dir, 0, sizeof (wnl_fh));
535 arg.name = fspath;
536 memset((char *)&clnt_res, 0, sizeof (clnt_res));
537 rpc_stat = clnt_call(clnt, WNLPROC_LOOKUP,
538 (xdrproc_t)xdr_wnl_diropargs, (caddr_t)&arg,
539 (xdrproc_t)xdr_wnl_diropres, (caddr_t)&clnt_res,
540 TIMEOUT);
541 if (rpc_stat == RPC_SUCCESS && clnt_res.status == WNL_OK)
542 return (SNEGO_DEF_VALID);
543 if (rpc_stat != RPC_AUTHERROR)
544 return (SNEGO_FAILURE);
545
546 {
547 struct rpc_err e;
548 wnl_diropres res;
549 char *p;
550 int tot = 0;
551
552 CLNT_GETERR(clnt, &e);
553 if (e.re_why != AUTH_TOOWEAK)
554 return (SNEGO_FAILURE);
555
556 if ((p = malloc(strlen(fspath)+3)) == NULL) {
557 syslog(LOG_ERR, "no memory\n");
558 return (SNEGO_FAILURE);
559 }
560 /*
561 * Do an x81 LOOKUP
562 */
563 p[0] = (char)WNL_SEC_NEGO;
564 strcpy(&p[2], fspath);
565 do {
566 p[1] = (char)(1+snego->cnt); /* sec index */
567 arg.name = p;
568 memset((char *)&res, 0, sizeof (wnl_diropres));
569 if (wnlproc_lookup_2(&arg, &res, clnt) !=
570 RPC_SUCCESS || res.status != WNL_OK) {
571 free(p);
572 return (SNEGO_FAILURE);
573 }
574
575 /*
576 * retrieve flavors from filehandle:
577 * 1st byte: length
578 * 2nd byte: status
579 * 3rd & 4th: pad
580 * 5th and after: sec flavors.
581 */
582 {
583 char *c = (char *)&res.wnl_diropres_u.
584 wnl_diropres.file;
585 int ii;
586 int cnt = ((int)*c)/sizeof (uint_t);
587 /* LINTED pointer alignment */
588 int *ip = (int *)(c+sizeof (int));
589
590 tot += cnt;
591 if (tot >= MAX_FLAVORS) {
592 free(p);
593 return (SNEGO_ARRAY_TOO_SMALL);
594 }
595 status = (int)*(c+1);
596 if (cnt > MAX_V2_CNT || cnt < 0) {
597 free(p);
598 return (SNEGO_FAILURE);
599 }
600 for (ii = 0; ii < cnt; ii++)
601 snego->array[snego->cnt+ii] =
602 ntohl(*(ip+ii));
603 snego->cnt += cnt;
604 }
605 } while (status);
606 free(p);
607 return (SNEGO_SUCCESS);
608 }
609 } else if (vers == WNL_V3) {
610 WNL_LOOKUP3args arg;
611 WNL_LOOKUP3res clnt_res;
612
613 memset((char *)&arg.what.dir, 0, sizeof (wnl_fh3));
614 arg.what.name = fspath;
615 arg.what.dir.data.data_len = 0;
616 arg.what.dir.data.data_val = 0;
617 memset((char *)&clnt_res, 0, sizeof (clnt_res));
618 rpc_stat = clnt_call(clnt, WNLPROC3_LOOKUP,
619 (xdrproc_t)xdr_WNL_LOOKUP3args, (caddr_t)&arg,
620 (xdrproc_t)xdr_WNL_LOOKUP3res, (caddr_t)&clnt_res,
621 TIMEOUT);
622 if (rpc_stat == RPC_SUCCESS && clnt_res.status == WNL3_OK)
623 return (SNEGO_DEF_VALID);
624 if (rpc_stat != RPC_AUTHERROR)
625 return (SNEGO_FAILURE);
626
627 {
628 struct rpc_err e;
629 WNL_LOOKUP3res res;
630 char *p;
631 int tot = 0;
632
633 CLNT_GETERR(clnt, &e);
634 if (e.re_why != AUTH_TOOWEAK)
635 return (SNEGO_FAILURE);
636
637 if ((p = malloc(strlen(fspath)+3)) == NULL) {
638 syslog(LOG_ERR, "no memory\n");
639 return (SNEGO_FAILURE);
640 }
641 /*
642 * Do an x81 LOOKUP
643 */
644 p[0] = (char)WNL_SEC_NEGO;
645 strcpy(&p[2], fspath);
646 do {
647 p[1] = (char)(1+snego->cnt); /* sec index */
648 arg.what.name = p;
649 memset((char *)&res, 0,
650 sizeof (WNL_LOOKUP3res));
651 if (wnlproc3_lookup_3(&arg, &res, clnt) !=
652 RPC_SUCCESS || res.status != WNL3_OK) {
653 free(p);
654 return (SNEGO_FAILURE);
655 }
656
657 /*
658 * retrieve flavors from filehandle:
659 *
660 * 1st byte: status
661 * 2nd thru 4th: pad
662 * 5th and after: sec flavors.
663 */
664 {
665 char *c = res.WNL_LOOKUP3res_u.
666 res_ok.object.data.data_val;
667 int ii;
668 int len = res.WNL_LOOKUP3res_u.res_ok.
669 object.data.data_len;
670 int cnt;
671 /* LINTED pointer alignment */
672 int *ip = (int *)(c+sizeof (int));
673
674 cnt = len/sizeof (uint_t) - 1;
675 tot += cnt;
676 if (tot >= MAX_FLAVORS) {
677 free(p);
678 return (SNEGO_ARRAY_TOO_SMALL);
679 }
680 status = (int)(*c);
681 if (cnt > MAX_V3_CNT || cnt < 0) {
682 free(p);
683 return (SNEGO_FAILURE);
684 }
685 for (ii = 0; ii < cnt; ii++)
686 snego->array[snego->cnt+ii] =
687 ntohl(*(ip+ii));
688 snego->cnt += cnt;
689 }
690 } while (status);
691 free(p);
692 return (SNEGO_SUCCESS);
693 }
694 }
695 return (SNEGO_FAILURE);
696 }
697 #endif
698
699 /*
700 * Get seconfig from /etc/nfssec.conf by name or by number or
701 * by descriptior.
702 */
703 /* ARGSUSED */
704 static int
get_seconfig(int whichway,char * name,int num,rpc_gss_service_t service,seconfig_t * entryp)705 get_seconfig(int whichway, char *name, int num,
706 rpc_gss_service_t service, seconfig_t *entryp)
707 {
708 char line[BUFSIZ]; /* holds each line of NFSSEC_CONF */
709 FILE *fp; /* file stream for NFSSEC_CONF */
710
711 if ((whichway == GETBYNAME) && (name == NULL))
712 return (SC_NOTFOUND);
713
714 (void) mutex_lock(&matching_lock);
715 if ((fp = fopen(NFSSEC_CONF, "r")) == NULL) {
716 (void) mutex_unlock(&matching_lock);
717 return (SC_OPENFAIL);
718 }
719
720 while (fgets(line, BUFSIZ, fp)) {
721 if (!(blank(line) || comment(line))) {
722 switch (whichway) {
723 case GETBYNAME:
724 if (matchname(line, name, entryp)) {
725 goto found;
726 }
727 break;
728
729 case GETBYNUM:
730 if (matchnum(line, num, entryp)) {
731 goto found;
732 }
733 break;
734
735 default:
736 break;
737 }
738 }
739 }
740 (void) fclose(fp);
741 (void) mutex_unlock(&matching_lock);
742 return (SC_NOTFOUND);
743
744 found:
745 (void) fclose(fp);
746 (void) mutex_unlock(&matching_lock);
747 (void) get_rpcnum(entryp);
748 return (SC_NOERROR);
749 }
750
751
752 /*
753 * NFS project private API.
754 * Get a seconfig entry from /etc/nfssec.conf by nfs specific sec name,
755 * e.g. des, krb5p, etc.
756 */
757 int
nfs_getseconfig_byname(char * secmode_name,seconfig_t * entryp)758 nfs_getseconfig_byname(char *secmode_name, seconfig_t *entryp)
759 {
760 if (!entryp)
761 return (SC_NOMEM);
762
763 return (get_seconfig(GETBYNAME, secmode_name, 0, rpc_gss_svc_none,
764 entryp));
765 }
766
767 /*
768 * NFS project private API.
769 *
770 * Get a seconfig entry from /etc/nfssec.conf by nfs specific sec number,
771 * e.g. AUTH_DES, AUTH_KRB5_P, etc.
772 */
773 int
nfs_getseconfig_bynumber(int nfs_secnum,seconfig_t * entryp)774 nfs_getseconfig_bynumber(int nfs_secnum, seconfig_t *entryp)
775 {
776 if (!entryp)
777 return (SC_NOMEM);
778
779 return (get_seconfig(GETBYNUM, NULL, nfs_secnum, rpc_gss_svc_none,
780 entryp));
781 }
782
783 /*
784 * NFS project private API.
785 *
786 * Get a seconfig_t entry used as the default for NFS operations.
787 * The default flavor entry is defined in /etc/nfssec.conf.
788 *
789 * Assume user has allocate spaces for secp.
790 */
791 int
nfs_getseconfig_default(seconfig_t * secp)792 nfs_getseconfig_default(seconfig_t *secp)
793 {
794 if (secp == NULL)
795 return (SC_NOMEM);
796
797 return (nfs_getseconfig_byname("default", secp));
798 }
799
800
801 /*
802 * NFS project private API.
803 *
804 * Free an sec_data structure.
805 * Free the parts that nfs_clnt_secdata allocates.
806 */
807 void
nfs_free_secdata(sec_data_t * secdata)808 nfs_free_secdata(sec_data_t *secdata)
809 {
810 dh_k4_clntdata_t *dkdata;
811 gss_clntdata_t *gdata;
812
813 if (!secdata)
814 return;
815
816 switch (secdata->rpcflavor) {
817 case AUTH_UNIX:
818 case AUTH_NONE:
819 break;
820
821 case AUTH_DES:
822 /* LINTED pointer alignment */
823 dkdata = (dh_k4_clntdata_t *)secdata->data;
824 if (dkdata) {
825 if (dkdata->netname)
826 free(dkdata->netname);
827 if (dkdata->syncaddr.buf)
828 free(dkdata->syncaddr.buf);
829 free(dkdata);
830 }
831 break;
832
833 case RPCSEC_GSS:
834 /* LINTED pointer alignment */
835 gdata = (gss_clntdata_t *)secdata->data;
836 if (gdata) {
837 if (gdata->mechanism.elements)
838 free(gdata->mechanism.elements);
839 free(gdata);
840 }
841 break;
842
843 default:
844 break;
845 }
846
847 free(secdata);
848 }
849
850 /*
851 * Make an client side sec_data structure and fill in appropriate value
852 * based on its rpc security flavor.
853 *
854 * It is caller's responsibility to allocate space for seconfig_t,
855 * and this routine will allocate space for the sec_data structure
856 * and related data field.
857 *
858 * Return the sec_data_t on success.
859 * If fail, return NULL pointer.
860 */
861 sec_data_t *
nfs_clnt_secdata(seconfig_t * secp,char * hostname,struct knetconfig * knconf,struct netbuf * syncaddr,int flags)862 nfs_clnt_secdata(seconfig_t *secp, char *hostname, struct knetconfig *knconf,
863 struct netbuf *syncaddr, int flags)
864 {
865 char netname[MAXNETNAMELEN+1];
866 sec_data_t *secdata;
867 dh_k4_clntdata_t *dkdata;
868 gss_clntdata_t *gdata;
869
870 secdata = malloc(sizeof (sec_data_t));
871 if (!secdata) {
872 syslog(LOG_ERR, "nfs_clnt_secdata: no memory\n");
873 return (NULL);
874 }
875 (void) memset(secdata, 0, sizeof (sec_data_t));
876
877 secdata->secmod = secp->sc_nfsnum;
878 secdata->rpcflavor = secp->sc_rpcnum;
879 secdata->uid = secp->sc_uid;
880 secdata->flags = flags;
881
882 /*
883 * Now, fill in the information for client side secdata :
884 *
885 * For AUTH_UNIX, AUTH_DES
886 * hostname can be in the form of
887 * nodename or
888 * nodename.domain
889 *
890 * For RPCSEC_GSS security flavor
891 * hostname can be in the form of
892 * nodename or
893 * nodename.domain or
894 * nodename@realm (realm can be the same as the domain) or
895 * nodename.domain@realm
896 */
897 switch (secp->sc_rpcnum) {
898 case AUTH_UNIX:
899 case AUTH_NONE:
900 secdata->data = NULL;
901 break;
902
903 case AUTH_DES:
904 /*
905 * If hostname is in the format of host.nisdomain
906 * the netname will be constructed with
907 * this nisdomain name rather than the default
908 * domain of the machine.
909 */
910 if (!host2netname(netname, hostname, NULL)) {
911 syslog(LOG_ERR, "host2netname: %s: unknown\n",
912 hostname);
913 goto err_out;
914 }
915 dkdata = malloc(sizeof (dh_k4_clntdata_t));
916 if (!dkdata) {
917 syslog(LOG_ERR,
918 "nfs_clnt_secdata: no memory\n");
919 goto err_out;
920 }
921 (void) memset((char *)dkdata, 0,
922 sizeof (dh_k4_clntdata_t));
923 if ((dkdata->netname = strdup(netname)) == NULL) {
924 syslog(LOG_ERR,
925 "nfs_clnt_secdata: no memory\n");
926 goto err_out;
927 }
928 dkdata->netnamelen = strlen(netname);
929 dkdata->knconf = knconf;
930 dkdata->syncaddr = *syncaddr;
931 dkdata->syncaddr.buf = malloc(syncaddr->len);
932 if (dkdata->syncaddr.buf == NULL) {
933 syslog(LOG_ERR,
934 "nfs_clnt_secdata: no memory\n");
935 goto err_out;
936 }
937 (void) memcpy(dkdata->syncaddr.buf, syncaddr->buf,
938 syncaddr->len);
939 secdata->data = (caddr_t)dkdata;
940 break;
941
942 case RPCSEC_GSS:
943 if (secp->sc_gss_mech_type == NULL) {
944 syslog(LOG_ERR,
945 "nfs_clnt_secdata: need mechanism information\n");
946 goto err_out;
947 }
948
949 gdata = malloc(sizeof (gss_clntdata_t));
950 if (!gdata) {
951 syslog(LOG_ERR,
952 "nfs_clnt_secdata: no memory\n");
953 goto err_out;
954 }
955
956 (void) strcpy(gdata->uname, "nfs");
957 if (!parsehostname(hostname, gdata->inst,
958 gdata->realm)) {
959 syslog(LOG_ERR,
960 "nfs_clnt_secdata: bad host name\n");
961 goto err_out;
962 }
963
964 gdata->mechanism.length =
965 secp->sc_gss_mech_type->length;
966 if (!(gdata->mechanism.elements =
967 malloc(secp->sc_gss_mech_type->length))) {
968 syslog(LOG_ERR,
969 "nfs_clnt_secdata: no memory\n");
970 goto err_out;
971 }
972 (void) memcpy(gdata->mechanism.elements,
973 secp->sc_gss_mech_type->elements,
974 secp->sc_gss_mech_type->length);
975
976 gdata->qop = secp->sc_qop;
977 gdata->service = secp->sc_service;
978 secdata->data = (caddr_t)gdata;
979 break;
980
981 default:
982 syslog(LOG_ERR, "nfs_clnt_secdata: unknown flavor\n");
983 goto err_out;
984 }
985
986 return (secdata);
987
988 err_out:
989 free(secdata);
990 return (NULL);
991 }
992
993 /*
994 * nfs_get_root_principal() maps a host name to its principal name
995 * based on the given security information.
996 *
997 * input : seconfig - security configuration information
998 * host - the host name which could be in the following forms:
999 * node
1000 * node.namedomain
1001 * node@secdomain (e.g. kerberos realm is a secdomain)
1002 * node.namedomain@secdomain
1003 * output : rootname_p - address of the principal name for the host
1004 *
1005 * Currently, this routine is only used by share program.
1006 *
1007 */
1008 bool_t
nfs_get_root_principal(seconfig_t * seconfig,char * host,caddr_t * rootname_p)1009 nfs_get_root_principal(seconfig_t *seconfig, char *host, caddr_t *rootname_p)
1010 {
1011 char netname[MAXNETNAMELEN+1], node[MAX_NAME_LEN];
1012 char secdomain[MAX_NAME_LEN];
1013 rpc_gss_principal_t gssname;
1014
1015 switch (seconfig->sc_rpcnum) {
1016 case AUTH_DES:
1017 if (!host2netname(netname, host, NULL)) {
1018 syslog(LOG_ERR,
1019 "nfs_get_root_principal: unknown host: %s\n", host);
1020 return (FALSE);
1021 }
1022 *rootname_p = strdup(netname);
1023 if (!*rootname_p) {
1024 syslog(LOG_ERR,
1025 "nfs_get_root_principal: no memory\n");
1026 return (FALSE);
1027 }
1028 break;
1029
1030 case RPCSEC_GSS:
1031 if (!parsehostname(host, node, secdomain)) {
1032 syslog(LOG_ERR,
1033 "nfs_get_root_principal: bad host name\n");
1034 return (FALSE);
1035 }
1036 if (!rpc_gss_get_principal_name(&gssname,
1037 seconfig->sc_gss_mech, "root", node, secdomain)) {
1038 syslog(LOG_ERR,
1039 "nfs_get_root_principal: can not get principal name : %s\n", host);
1040 return (FALSE);
1041 }
1042
1043 *rootname_p = (caddr_t)gssname;
1044 break;
1045
1046 default:
1047 return (FALSE);
1048 }
1049 return (TRUE);
1050 }
1051
1052
1053 /*
1054 * SYSLOG SC_* errors.
1055 */
1056 int
nfs_syslog_scerr(int scerror,char msg[])1057 nfs_syslog_scerr(int scerror, char msg[])
1058 {
1059 switch (scerror) {
1060 case SC_NOMEM :
1061 sprintf(msg, "%s : no memory", NFSSEC_CONF);
1062 return (0);
1063 case SC_OPENFAIL :
1064 sprintf(msg, "can not open %s", NFSSEC_CONF);
1065 return (0);
1066 case SC_NOTFOUND :
1067 sprintf(msg, "has no entry in %s", NFSSEC_CONF);
1068 return (0);
1069 case SC_BADENTRIES :
1070 sprintf(msg, "bad entry in %s", NFSSEC_CONF);
1071 return (0);
1072 default:
1073 msg[0] = '\0';
1074 return (-1);
1075 }
1076 }
1077