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