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