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