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