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