xref: /illumos-gate/usr/src/cmd/fs.d/nfs/lib/nfs_sec.c (revision 5db531e3faa94427746eae754b11770fd8416b6d)
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
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
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
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
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 *
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
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
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
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
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 *
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 *
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
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
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
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
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
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
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 *
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
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
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