1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* 24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 /* 31 * This contains miscellaneous functions moved from commands to the library. 32 */ 33 34 #include "mt.h" 35 #include <stdlib.h> 36 #include <stdio.h> 37 #include <syslog.h> 38 #include <string.h> 39 #include <unistd.h> 40 #include <gssapi/gssapi.h> 41 #include <rpc/rpc.h> 42 #include <rpcsvc/nis.h> 43 #include <rpcsvc/nis_dhext.h> 44 #include <rpc/auth.h> 45 #include <rpc/auth_sys.h> 46 #include <rpc/auth_des.h> 47 #include <rpc/key_prot.h> 48 #include <netdir.h> 49 #include <netconfig.h> 50 #include <sys/socket.h> 51 #include <netinet/in.h> 52 #include <netdb.h> 53 #include <dlfcn.h> 54 #include <gssapi/gssapi.h> 55 #include "nis_local.h" 56 57 extern int bin2hex(int len, unsigned char *binnum, char *hexnum); 58 extern int hex2bin(int len, char *hexnum, char *binnum); 59 60 /* 61 * Returns the NIS principal name of the person making the request 62 * XXX This is set up to use Secure RPC only at the moment, it should 63 * be possible for any authentication scheme to be incorporated if it 64 * has a "full name" that we can return as the principal name. 65 */ 66 static const nis_name nobody = "nobody"; 67 68 static NIS_HASH_TABLE credtbl; 69 struct creditem { 70 NIS_HASH_ITEM item; 71 char pname[1024]; 72 }; 73 74 static void 75 add_cred_item(char *netname, char *pname) 76 { 77 struct creditem *foo = NULL, *old = NULL; 78 79 if (strlen(pname) >= sizeof (foo->pname)) { 80 syslog(LOG_ERR, 81 "add_cred_item: principal name too long '%s'", 82 pname); 83 return; 84 } 85 86 old = (struct creditem *)nis_find_item(netname, &credtbl); 87 if (old != NULL) 88 return; 89 90 foo = calloc(1, sizeof (struct creditem)); 91 if (foo == NULL) 92 return; 93 94 foo->item.name = strdup(netname); 95 if (foo->item.name == NULL) { 96 free(foo); 97 return; 98 } 99 100 (void) strcpy(foo->pname, pname); 101 (void) nis_insert_item((NIS_HASH_ITEM *)foo, &credtbl); 102 } 103 104 static bool_t 105 find_cred_item(char *netname, char *pname) 106 { 107 struct creditem *old = NULL; 108 109 if (strlen(pname) >= sizeof (old->pname)) 110 return (FALSE); 111 112 old = (struct creditem *)nis_find_item(netname, &credtbl); 113 if (old == NULL) 114 return (FALSE); 115 (void) strcpy(pname, old->pname); 116 return (TRUE); 117 } 118 119 static bool_t 120 delete_cred_item(char *netname) 121 { 122 struct creditem *toremove = NULL; 123 124 if (toremove = (struct creditem *)nis_remove_item(netname, 125 &credtbl)) { 126 free(toremove->item.name); 127 free(toremove); 128 return (TRUE); 129 } else 130 return (FALSE); 131 } 132 133 void 134 __nis_auth2princ( 135 char *name, 136 int flavor, 137 caddr_t auth, 138 bool_t refresh, 139 int verbose) 140 { 141 struct authsys_parms *au; 142 struct authdes_cred *ad; 143 char *rmtdomain; 144 char srch[2048]; /* search criteria */ 145 nis_result *res; 146 147 srch[0] = '\0'; 148 149 (void) strcpy(name, nobody); /* default is "nobody" */ 150 if (flavor == AUTH_NONE) { 151 if (verbose) { 152 syslog(LOG_INFO, 153 "__nis_auth2princ: flavor = NONE: returning '%s'", nobody); 154 } 155 return; 156 } else if (flavor == AUTH_SYS) { /* XXX ifdef this for 4.1 */ 157 /* LINTED pointer cast */ 158 au = (struct authsys_parms *)(auth); 159 rmtdomain = nis_domain_of(au->aup_machname); 160 if (au->aup_uid == 0) { 161 (void) snprintf(name, MAX_MACHINE_NAME, 162 "%s", au->aup_machname); 163 if (!rmtdomain) 164 (void) strcat(name, __nis_rpc_domain()); 165 if (name[strlen(name) - 1] != '.') 166 (void) strcat(name, "."); 167 if (verbose) { 168 syslog(LOG_INFO, 169 "__nis_auth2princ: flavor = SYS: returning '%s'", name); 170 } 171 return; 172 } 173 (void) snprintf(srch, 174 sizeof (srch) - 1, 175 "[auth_name=\"%d\", auth_type=LOCAL], cred.org_dir.%s", 176 (int)au->aup_uid, (*rmtdomain == '.') ? 177 (char *)nis_local_directory() : rmtdomain); 178 if (srch[strlen(srch) - 1] != '.') { 179 (void) strcat(srch, "."); 180 } 181 } else if (flavor == AUTH_DES) { 182 /* LINTED pointer cast */ 183 ad = (struct authdes_cred *)(auth); 184 if (refresh) 185 (void) delete_cred_item(ad->adc_fullname.name); 186 else 187 if (find_cred_item(ad->adc_fullname.name, name)) { 188 if (verbose) 189 syslog(LOG_INFO, 190 "__nis_auth2princ: flavor = DES: returning from cache '%s'", 191 name); 192 return; 193 } 194 195 rmtdomain = strchr(ad->adc_fullname.name, '@'); 196 if (rmtdomain) { 197 rmtdomain++; 198 (void) snprintf(srch, 199 sizeof (srch) - 1, 200 "[auth_name=%s, auth_type=DES], cred.org_dir.%s", 201 ad->adc_fullname.name, rmtdomain); 202 if (srch[strlen(srch) - 1] != '.') { 203 (void) strcat(srch, "."); 204 } 205 } else { 206 if (verbose) { 207 syslog(LOG_INFO, 208 "__nis_auth2princ: flavor = DES: returning '%s'", 209 nobody); 210 } 211 return; 212 } 213 } else { 214 syslog(LOG_WARNING, 215 "__nis_auth2princ: flavor = %d(unknown): returning '%s'", 216 flavor, nobody); 217 return; 218 } 219 if (verbose) 220 syslog(LOG_INFO, 221 "__nis_auth2princ: calling list with name '%s'", 222 name); 223 res = nis_list(srch, NO_AUTHINFO+USE_DGRAM+FOLLOW_LINKS, NULL, NULL); 224 if (res->status != NIS_SUCCESS) { 225 if (verbose) 226 syslog(LOG_INFO, 227 "__nis_auth2princ: error doing nis_list: %s", 228 nis_sperrno(res->status)); 229 } else { 230 (void) strncpy(name, ENTRY_VAL(res->objects.objects_val, 0), 231 1024); 232 if (flavor == AUTH_DES) 233 add_cred_item(ad->adc_fullname.name, name); 234 } 235 236 nis_freeresult(res); 237 if (verbose) 238 syslog(LOG_INFO, 239 "__nis_auth2princ: flavor = %s: returning : '%s'", 240 flavor == AUTH_SYS? "SYS" : "DES", name); 241 } 242 243 #define MECH_LIB_PREFIX1 "/usr/lib/" 244 245 #ifdef _LP64 246 247 #define MECH_LIB_PREFIX2 "64/" 248 249 #else /* _LP64 */ 250 251 #define MECH_LIB_PREFIX2 "" 252 253 #endif /* _LP64 */ 254 255 #define MECH_LIB_DIR "gss/" 256 257 #define MECH_LIB_PREFIX MECH_LIB_PREFIX1 MECH_LIB_PREFIX2 258 259 #define MECHDH MECH_LIB_PREFIX MECH_LIB_DIR "mech_dh.so.1" 260 #define LIBGSS MECH_LIB_PREFIX "libgss.so.1" 261 262 static gss_OID_desc __dh_gss_c_nt_netname = { 263 9, "\053\006\004\001\052\002\032\001\001" 264 }; 265 266 mutex_t gss_load_lock = DEFAULTMUTEX; 267 static gss_OID GSS_EXPORT_NAME = 0; 268 static gss_OID DH_NETNAME = &__dh_gss_c_nt_netname; 269 270 typedef OM_uint32 (*gss_fptr)(); 271 OM_uint32 (*g_import_name)(); 272 OM_uint32 (*g_display_name)(); 273 OM_uint32 (*g_release_name)(); 274 OM_uint32 (*g_release_buffer)(); 275 OM_uint32 (*g_release_oid)(); 276 277 /* 278 * gss_OID_load() 279 * 280 * This routine is called by __nis_gssprin2netname to define values for 281 * the gss-api-export-name OID, the Diffie-Hellman netname OID, and 282 * the gss support routines that it needs. 283 * The reason for this support routine is that libnsl cannot have an 284 * explicit dependency on libgss. Callers of __nisgssprin2netname are 285 * expected to have loaded libgss through the rpcsec layer. The work around 286 * is to dlopen the needed shared objects and grab the symbols with dlsym. 287 * This routine opens libgss RTLD_NOLOAD. If this fails then libgss.so.1 288 * is not loaded and we return error. Otherwise it uses dlsym to 289 * defines GSS_EXPORT_NAME to have the value of GSS_C_NT_EXPORT_NAME and 290 * to assign the above fuction pointers. 291 * If this succeeds then the routine will attempt to load mech_dh.so.1 292 * and over ride DH_NETNAME with the value of __DH_GSS_C_NT_NETNAME from 293 * that shared object. We don't consider it an error if this fails because 294 * its conceivable that another mechanism backend will support the netname 295 * name type and mech_dh.so.1 not be available. 296 * 297 * Return 0 on failer, 1 on success. 298 */ 299 300 static int 301 gss_OID_load() 302 { 303 void *dh; 304 gss_OID *OIDptr; 305 int stat = 0; 306 307 (void) mutex_lock(&gss_load_lock); 308 if (GSS_EXPORT_NAME) { 309 (void) mutex_unlock(&gss_load_lock); 310 return (0); 311 } 312 313 /* if LIBGSS is not loaded return an error */ 314 if ((dh = dlopen(LIBGSS, RTLD_NOLOAD)) == NULL) { 315 (void) mutex_unlock(&gss_load_lock); 316 return (0); 317 } 318 319 OIDptr = (gss_OID *)dlsym(dh, "GSS_C_NT_EXPORT_NAME"); 320 if (OIDptr) 321 GSS_EXPORT_NAME = *OIDptr; 322 else 323 goto Done; 324 325 g_import_name = (gss_fptr)dlsym(dh, "gss_import_name"); 326 if (g_import_name == 0) 327 goto Done; 328 329 g_display_name = (gss_fptr)dlsym(dh, "gss_display_name"); 330 if (g_display_name == 0) 331 goto Done; 332 333 g_release_name = (gss_fptr)dlsym(dh, "gss_release_name"); 334 if (g_release_name == 0) 335 goto Done; 336 337 g_release_buffer = (gss_fptr)dlsym(dh, "gss_release_buffer"); 338 if (g_release_buffer == 0) 339 goto Done; 340 341 g_release_oid = (gss_fptr)dlsym(dh, "gss_release_oid"); 342 if (g_release_oid == 0) 343 goto Done; 344 345 stat = 1; 346 /* 347 * Try and get the official netname oid from mech_dh.so. 348 * If this fails will just keep our default from above. 349 */ 350 351 if ((dh = dlopen(MECHDH, RTLD_LAZY)) != NULL) { 352 353 OIDptr = (gss_OID *)dlsym(dh, "__DH_GSS_C_NT_NETNAME"); 354 if (OIDptr) 355 DH_NETNAME = *OIDptr; 356 } 357 358 Done: 359 (void) mutex_unlock(&gss_load_lock); 360 361 if (stat == 0) 362 GSS_EXPORT_NAME = 0; 363 364 return (stat); 365 } 366 367 368 /* 369 * int 370 * __nis_gssprin2netname(rpc_gss_principal_t prin, 371 * char netname[MAXNETNAMELEN+1]) 372 * 373 * This routine attempts to extract the netname from an rpc_gss_principal_t 374 * which is in { gss-api-exorted-name } format. Return 0 if a netname was 375 * found, else return -1. 376 */ 377 378 /* 379 * This routine has a dependency on libgss.so. So we will pragma weak 380 * the interfaces that we need. When this routine is called libgss 381 * should have been loaded by the rpcsec layer. We will call gss_OID_load 382 * to get the value for GSS_EXPORT_NAME. If gss_OID_load failes return -1. 383 */ 384 385 #define OID_IS_EQUAL(o1, o2) ((o1) && (o2) && \ 386 ((o1)->length == (o2)->length) && \ 387 (memcmp((o1)->elements, (o2)->elements, (o1)->length) == 0)) 388 389 int 390 __nis_gssprin2netname(rpc_gss_principal_t prin, char netname[MAXNETNAMELEN+1]) 391 { 392 gss_buffer_desc display_name; 393 gss_name_t name; 394 gss_OID name_type; 395 gss_buffer_desc expName; 396 int stat = -1; 397 OM_uint32 major, minor; 398 399 /* See if we already got the OID */ 400 if (GSS_EXPORT_NAME == 0) { 401 /* Nope. See if GSS is loaded and get the OIDs */ 402 if (!gss_OID_load()) 403 return (-1); /* if libgss.so.1 isn't loaded */ 404 } 405 406 expName.length = prin->len; 407 expName.value = prin->name; 408 409 major = (*g_import_name)(&minor, &expName, 410 (gss_OID) GSS_EXPORT_NAME, &name); 411 412 if (major == GSS_S_COMPLETE) { 413 major = (*g_display_name)(&minor, name, 414 &display_name, &name_type); 415 416 /* We're done with the gss_internal name */ 417 (void) (*g_release_name)(&minor, &name); 418 419 if (major == GSS_S_COMPLETE) { 420 /* 421 * Check if we've got a netname. If we do we copy it 422 * and make sure that its null terminated. 423 */ 424 if (OID_IS_EQUAL(DH_NETNAME, name_type)) { 425 (void) strncpy(netname, 426 (char *)display_name.value, 427 MAXNETNAMELEN); 428 netname[MAXNETNAMELEN] = '\0'; 429 stat = 0; 430 } 431 /* 432 * If there are other display formats that can 433 * be converted to netnames easily, insert here. 434 * 435 * else if (OID_IS_EQUAL(OTHER_NT_OID, name_type)) { 436 * convert2netname(display_name.value, netname); 437 * } ... 438 */ 439 440 /* Release temporty storage */ 441 (void) (*g_release_buffer)(&minor, &display_name); 442 (void) (*g_release_oid)(&minor, &name_type); 443 } 444 } 445 446 if (stat == 0) 447 return (stat); 448 449 /* 450 * If we got here then prin is not a gss netname type. Currently 451 * other types are not supported. To support the general case the 452 * prin type needs to be looked up in a table that will map an 453 * gss-api-export-name to a netname. The guts of the routine to do 454 * this, would look like this: 455 * 456 * char xport[NIS_MAXNAMELEN]; 457 * char query[NIS_MAXNAMELEN]; 458 * stat = -1; 459 * nis_result *r; 460 * 461 * bin2hex(expName.length, expName.value, xport); 462 * sprintf(query, "[gssprincipal=%s],gssprin.org_dir.%s", xport, 463 * nis_local_directory()); 464 * r = nis_list(query, 0, 0, 0); 465 * if (r->status == NIS_SUCCESS) { 466 * stat = 0; 467 * strcpy(netname, ENTRY_VAL(r->objects.object_val, 1); 468 * } 469 * nis_freeresult(r); 470 * return (stat); 471 * 472 * Here it is assumed that the gssprin table containes two columns. 473 * The first, gssprincipal, contains the exported gss principal name 474 * in hex format. And the second, netname, that contains the secure 475 * rpc netname. 476 */ 477 478 return (stat); 479 } 480 481 static char * 482 flavor2str(int flavor) 483 { 484 switch (flavor) { 485 case AUTH_NONE: 486 return ("NONE"); 487 case AUTH_SYS: 488 return ("SYS"); 489 case AUTH_DES: 490 return ("DES"); 491 case RPCSEC_GSS: 492 return ("GSS"); 493 default: 494 return ("unknown"); 495 } 496 } 497 498 /* 499 * Based on __nis_auth2princ but this one has RPCSEC_GSS support. 500 */ 501 void 502 __nis_auth2princ_rpcgss( 503 char *name, /* out */ 504 struct svc_req *req, /* in */ 505 bool_t refresh, /* in */ 506 int verbose) /* in */ 507 { 508 struct authsys_parms *au; 509 struct authdes_cred *ad; 510 char *rmtdomain; 511 char srch[2048]; /* search criteria */ 512 nis_result *res; 513 caddr_t auth; 514 char auth_type[MECH_MAXATNAME]; /* cred tbl field */ 515 char netname[MAXNETNAMELEN+1] = {0}; /* RPC netnm */ 516 int flavor; /* secure RPC flavor */ 517 518 srch[0] = '\0'; 519 520 if (req) { 521 flavor = req->rq_cred.oa_flavor; 522 auth = req->rq_clntcred; 523 } else { 524 if (verbose) 525 syslog(LOG_ERR, 526 "_auth2princ_rpcgss: req = NULL: returning '%s'", 527 nobody); 528 return; 529 } 530 (void) strcpy(name, nobody); /* default is "nobody" */ 531 if (flavor == AUTH_NONE) { 532 if (verbose) { 533 syslog(LOG_INFO, 534 "__nis_auth2princ_rpcgss: flavor = NONE: returning '%s'", 535 nobody); 536 } 537 return; 538 } else if (flavor == AUTH_SYS) { /* XXX ifdef this for 4.1 */ 539 /* LINTED pointer cast */ 540 au = (struct authsys_parms *)(auth); 541 rmtdomain = nis_domain_of(au->aup_machname); 542 if (au->aup_uid == 0) { 543 (void) snprintf(name, MAX_MACHINE_NAME, 544 "%s", au->aup_machname); 545 if (!rmtdomain) 546 (void) strcat(name, __nis_rpc_domain()); 547 if (name[strlen(name) - 1] != '.') 548 (void) strcat(name, "."); 549 if (verbose) { 550 syslog(LOG_INFO, 551 "__nis_auth2princ_rpcgss: flavor = SYS: returning '%s'", name); 552 } 553 return; 554 } 555 (void) snprintf(srch, 556 sizeof (srch) - 1, 557 "[auth_name=\"%ld\", auth_type=LOCAL], cred.org_dir.%s", 558 au->aup_uid, (*rmtdomain == '.') ? 559 (char *)nis_local_directory() : rmtdomain); 560 if (srch[strlen(srch) - 1] != '.') { 561 (void) strcat(srch, "."); 562 } 563 } else if (flavor == AUTH_DES) { 564 /* LINTED pointer cast */ 565 ad = (struct authdes_cred *)(auth); 566 if (refresh) 567 (void) delete_cred_item(ad->adc_fullname.name); 568 else 569 if (find_cred_item(ad->adc_fullname.name, name)) { 570 if (verbose) 571 syslog(LOG_INFO, 572 "__nis_auth2princ_rpcgss: flavor = DES: returning from cache '%s'", 573 name); 574 return; 575 } 576 577 rmtdomain = strchr(ad->adc_fullname.name, '@'); 578 if (rmtdomain) { 579 rmtdomain++; 580 (void) snprintf(srch, 581 sizeof (srch) - 1, 582 "[auth_name=%s, auth_type=DES], cred.org_dir.%s", 583 ad->adc_fullname.name, rmtdomain); 584 if (srch[strlen(srch) - 1] != '.') { 585 (void) strcat(srch, "."); 586 (void) strncpy(netname, ad->adc_fullname.name, 587 sizeof (netname)); 588 netname[sizeof (netname) - 1] = '\0'; 589 } 590 } else { 591 if (verbose) { 592 syslog(LOG_INFO, 593 "__nis_auth2princ_rpcgss: flavor = DES: returning '%s'", 594 nobody); 595 } 596 return; 597 } 598 } else if (flavor == RPCSEC_GSS) { 599 rpc_gss_rawcred_t *rcred; 600 void *cookie; 601 602 if (!rpc_gss_getcred(req, &rcred, NULL, &cookie)) { 603 if (verbose) { 604 syslog(LOG_WARNING, 605 "__nis_auth2princ_rpcgss: GSS getcred failure: returning '%s'", 606 nobody); 607 } 608 return; 609 } 610 611 if (__nis_gssprin2netname(rcred->client_principal, netname) 612 < 0) { 613 syslog(LOG_ERR, 614 "__nis_auth2princ_rpcgss: can't extract netname from gss cred: returning '%s'", 615 nobody); 616 return; 617 } 618 619 if (refresh) 620 (void) delete_cred_item(netname); 621 else 622 if (find_cred_item(netname, name)) { 623 if (verbose) 624 syslog(LOG_INFO, 625 "__nis_auth2princ_rpcgss: flavor = RPCSEC_GSS: returning from cache '%s'", 626 name); 627 return; 628 } 629 630 rmtdomain = strchr(netname, '@'); 631 if (rmtdomain) { 632 char alias[MECH_MAXALIASNAME+1] = { 0 }; 633 634 rmtdomain++; 635 if (!__nis_mechname2alias(rcred->mechanism, alias, 636 sizeof (alias))) { 637 syslog(LOG_ERR, 638 "__nis_auth2princ_rpcgss: mechname '%s' not found: returning 'nobody'", 639 rcred->mechanism); 640 return; 641 } 642 643 if (alias[0] != '\0') { 644 (void) __nis_mechalias2authtype(alias, 645 auth_type, sizeof (auth_type)); 646 647 (void) snprintf(srch, sizeof (srch) - 1, 648 "[auth_name=%s, auth_type=%s], cred.org_dir.%s", 649 netname, auth_type, rmtdomain); 650 651 if (srch[strlen(srch) - 1] != '.') { 652 (void) strcat(srch, "."); 653 } 654 } else { 655 syslog(LOG_ERR, 656 "__nis_auth2princ_rpcgss: no alias found for mechname '%s': returning 'nobody'", 657 rcred->mechanism); 658 return; 659 } 660 } else { 661 if (verbose) { 662 syslog(LOG_INFO, 663 "__nis_auth2princ_rpcgss: flavor = RPCSEC_GSS: returning '%s'", 664 nobody); 665 } 666 return; 667 } 668 } else { 669 syslog(LOG_WARNING, 670 "__nis_auth2princ_rpcgss: flavor = %d(unknown): returning '%s'", 671 flavor, nobody); 672 return; 673 } 674 if (verbose) 675 if (flavor == AUTH_DES || flavor == RPCSEC_GSS) 676 syslog(LOG_INFO, 677 "__nis_auth2princ_rpcgss: calling list with name '%s' and type '%s'", 678 netname, 679 flavor == AUTH_DES ? "DES" : auth_type); 680 else /* AUTH_SYS */ 681 syslog(LOG_INFO, 682 "__nis_auth2princ_rpcgss: calling list with uid (LOCAL) '%d'", 683 au->aup_uid); 684 685 res = nis_list(srch, NO_AUTHINFO+USE_DGRAM+FOLLOW_LINKS, NULL, NULL); 686 if (res->status != NIS_SUCCESS) { 687 if (verbose) 688 syslog(LOG_INFO, 689 "__nis_auth2princ_rpcgss: error doing nis_list: %s", 690 nis_sperrno(res->status)); 691 } else { 692 (void) strncpy(name, ENTRY_VAL(res->objects.objects_val, 0), 693 1024); 694 if (flavor == AUTH_DES || flavor == RPCSEC_GSS) { 695 if (verbose) 696 syslog(LOG_INFO, 697 "__nis_auth2princ_rpcgss: caching '%s'/'%s'\n", 698 netname, name); 699 add_cred_item(netname, name); 700 } 701 } 702 703 nis_freeresult(res); 704 if (verbose) 705 syslog(LOG_INFO, 706 "__nis_auth2princ_rpcgss: flavor = %s: returning : '%s'", 707 flavor2str(flavor), name); 708 709 } 710 711 /* 712 * This function returns true if the given principal has the right to 713 * do the requested function on the given object. It could be a define 714 * if that would save time. At the moment it is a function. 715 * NOTE: It recursively calls NIS by doing the lookup on the group if 716 * the conditional gets that far. 717 * 718 * N.B. If the principal passed is 'null' then we're recursing and don't 719 * need to check it. (we always let ourselves look at the data) 720 */ 721 bool_t 722 __nis_ck_perms( 723 unsigned int right, /* The Access right we desire */ 724 unsigned int mask, /* The mask for World/Group/Owner */ 725 nis_object *obj, /* A pointer to the object */ 726 nis_name pr, /* Principal making the request */ 727 int level) /* security level server is running at */ 728 { 729 if ((level == 0) || (*pr == 0)) 730 return (TRUE); 731 732 return (NIS_NOBODY(mask, right) || 733 (NIS_WORLD(mask, right) && (strcmp(pr, "nobody") != 0)) || 734 (NIS_OWNER(mask, right) && 735 (nis_dir_cmp(pr, obj->zo_owner) == SAME_NAME)) || 736 (NIS_GROUP(mask, right) && 737 (strlen(obj->zo_group) > (size_t)(1)) && 738 __do_ismember(pr, obj, nis_lookup))); 739 } 740 741 /* 742 * is 'host' the master server for "org_dir."'domain' ? 743 */ 744 bool_t 745 __nis_ismaster(char *host, char *domain) 746 { 747 nis_server **srvs; /* servers that serve 'domain' */ 748 nis_server *master_srv; 749 char buf[NIS_MAXNAMELEN]; 750 bool_t res; 751 752 if (domain == NULL) { 753 syslog(LOG_ERR, "__nis_ismaster(): null domain"); 754 return (FALSE); 755 } 756 /* strlen(".org_dir") + null + "." = 10 */ 757 if ((strlen(domain) + 10) > (size_t)NIS_MAXNAMELEN) 758 return (FALSE); 759 760 (void) snprintf(buf, sizeof (buf), "org_dir.%s", domain); 761 if (buf[strlen(buf) - 1] != '.') 762 (void) strcat(buf, "."); 763 764 srvs = nis_getservlist(buf); 765 if (srvs == NULL) { 766 /* can't find any of the servers that serve this domain */ 767 /* something is very wrong ! */ 768 syslog(LOG_ERR, 769 "cannot get a list of servers that serve '%s'", 770 buf); 771 return (FALSE); 772 } 773 master_srv = srvs[0]; /* the first one is always the master */ 774 775 if (strcasecmp(host, master_srv->name) == 0) 776 res = TRUE; 777 else 778 res = FALSE; 779 780 /* done with server list */ 781 nis_freeservlist(srvs); 782 783 return (res); 784 } 785 786 /* 787 * check if this principal is the owner of the table 788 * or is a member of the table group owner 789 */ 790 bool_t 791 __nis_isadmin(char *princ, char *table, char *domain) 792 { 793 char buf[NIS_MAXNAMELEN]; 794 struct nis_result *res; 795 struct nis_object *obj; 796 bool_t ans = FALSE; 797 798 if ((princ == NULL || *princ == '\0') || 799 (table == NULL || *table == '\0') || 800 (domain == NULL || *domain == '\0')) 801 return (FALSE); 802 803 /* strlen(".org_dir.") + null + "." = 11 */ 804 if ((strlen(table) + strlen(domain) + 11) > 805 (size_t)NIS_MAXNAMELEN) { 806 syslog(LOG_ERR, "__nis_isadmin: buffer too small"); 807 return (FALSE); 808 } 809 (void) snprintf(buf, sizeof (buf), "%s.org_dir.%s", table, domain); 810 if (buf[strlen(buf) - 1] != '.') 811 (void) strcat(buf, "."); 812 813 /* get the table object */ 814 res = nis_lookup(buf, FOLLOW_LINKS); 815 if (res->status != NIS_SUCCESS) { 816 syslog(LOG_ERR, 817 "__nis_isadmin: could not lookup '%s' table", 818 table); 819 nis_freeresult(res); 820 return (FALSE); 821 } 822 obj = NIS_RES_OBJECT(res); 823 if (obj->zo_data.zo_type != NIS_TABLE_OBJ) { 824 syslog(LOG_ERR, "__nis_isadmin: not a table object"); 825 nis_freeresult(res); 826 return (FALSE); 827 } 828 if ((strcasecmp(obj->zo_owner, princ) == 0) || 829 ((obj->zo_group) && (*(obj->zo_group)) && 830 nis_ismember(princ, obj->zo_group))) 831 ans = TRUE; 832 833 nis_freeresult(res); 834 return (ans); 835 } 836 837 #define NIS_NOHOSTNAME 48 838 #define NIS_NOHOSTADDR 49 839 840 nis_server * 841 __nis_host2nis_server( 842 char *host, /* host name */ 843 bool_t addpubkey, /* add pub key info */ 844 int *errcode) /* error code */ 845 { 846 return (__nis_host2nis_server_g(host, addpubkey, TRUE, 847 errcode)); 848 } 849 850 static void 851 nis_free_endpoints(endpoint *ep, int n) 852 { 853 int i; 854 855 if (ep != NULL) { 856 for (i = 0; i < n; i++) { 857 if (ep[i].uaddr != NULL) 858 free(ep[i].uaddr); 859 if (ep[i].family != NULL) 860 free(ep[i].family); 861 if (ep[i].proto != NULL) 862 free(ep[i].proto); 863 } 864 free(ep); 865 } 866 } 867 868 void 869 __free_nis_server(nis_server *server) 870 { 871 if (server != NULL) { 872 free(server->name); 873 nis_free_endpoints(server->ep.ep_val, 874 server->ep.ep_len); 875 free(server->pkey.n_bytes); 876 free(server); 877 } 878 } 879 880 /* 881 * This function constructs a server description of the host 882 * given (or the local host) and returns it as a nis_server 883 * structure. 884 * Returns NULL on error, and sets the errcode. 885 */ 886 nis_server * 887 __nis_host2nis_server_g(const char *host, 888 bool_t addpubkey, 889 bool_t loopback, 890 int *errcode) 891 { 892 #define INC_SIZE 512 893 int addr_size = 0; 894 endpoint *addr, *oldaddr; 895 nis_server *hostinfo; 896 int num_ep = 0, i; 897 struct netconfig *nc; 898 void *nch; 899 struct nd_hostserv hs; 900 struct nd_addrlist *addrlist; 901 char hostnetname[NIS_MAXPATH]; 902 struct hostent *he; 903 char netname[MAXNETNAMELEN]; 904 char hostname[MAXHOSTNAMELEN+1]; 905 char pkey[HEXKEYBYTES+1]; 906 mechanism_t **mechlist; 907 size_t hlen; 908 909 if (host) { 910 he = gethostbyname(host); 911 if (!he) { 912 if (errcode) 913 *errcode = NIS_BADNAME; 914 return (NULL); 915 } 916 917 hlen = strlen(host); 918 if (hlen + 1 >= sizeof (hostnetname) || 919 hlen >= sizeof (hostname)) { 920 if (errcode) 921 *errcode = NIS_BADNAME; 922 return (NULL); 923 } 924 (void) strcpy(hostname, host); 925 hs.h_host = hostname; 926 927 /* 928 * Make attempt to fully qualify hostname. If hostname 929 * contains a dot, then assume it is already fully 930 * qualified and just add a trailing dot. Otherwise, 931 * append local domain name. 932 */ 933 934 (void) strcpy(hostnetname, host); 935 if (strchr(hostnetname, '.') == 0) { 936 char *localDir = nis_local_directory(); 937 size_t reqlen = hlen + strlen(localDir); 938 if (*localDir != '.') { 939 (void) strcat(hostnetname, "."); 940 reqlen += 1; 941 } 942 if (reqlen >= sizeof (hostnetname)) { 943 if (errcode) 944 *errcode = NIS_BADNAME; 945 return (NULL); 946 } 947 (void) strcat(hostnetname, localDir); 948 } 949 if (hostnetname[strlen(hostnetname)-1] != '.') 950 (void) strcat(hostnetname, "."); 951 } else { 952 if (gethostname(hostname, sizeof (hostname)) != 0) { 953 if (errcode) 954 *errcode = NIS_NOHOSTNAME; 955 return (NULL); 956 } 957 hs.h_host = HOST_SELF_CONNECT; 958 } 959 960 if (!(nch = setnetconfig())) 961 return (NULL); 962 963 hs.h_serv = "rpcbind"; 964 965 addr = NULL; 966 while (nc = getnetconfig(nch)) { 967 if (!loopback && (strcmp(nc->nc_protofmly, NC_LOOPBACK) == 0)) 968 continue; 969 if (netdir_getbyname(nc, &hs, &addrlist)) 970 continue; 971 for (i = 0; i < addrlist->n_cnt; i++, num_ep++) { 972 if (num_ep == addr_size) { 973 addr_size += INC_SIZE; 974 oldaddr = addr; 975 addr = realloc(addr, 976 addr_size * sizeof (endpoint)); 977 if (addr == NULL) { 978 if (errcode) 979 *errcode = NIS_NOMEMORY; 980 (void) endnetconfig(nch); 981 nis_free_endpoints(oldaddr, num_ep); 982 netdir_free((char *)addrlist, ND_ADDRLIST); 983 return (NULL); 984 } 985 } 986 addr[num_ep].uaddr = 987 taddr2uaddr(nc, &(addrlist->n_addrs[i])); 988 if (!addr[num_ep].uaddr) { 989 if (errcode) 990 *errcode = NIS_NOMEMORY; 991 (void) endnetconfig(nch); 992 nis_free_endpoints(addr, num_ep); 993 netdir_free((char *)addrlist, ND_ADDRLIST); 994 return (NULL); 995 } 996 __nis_netconfig2ep(nc, &(addr[num_ep])); 997 } 998 netdir_free((char *)addrlist, ND_ADDRLIST); 999 } 1000 (void) endnetconfig(nch); 1001 1002 if ((hostinfo = calloc(1, sizeof (nis_server))) == NULL) { 1003 nis_free_endpoints(addr, num_ep); 1004 if (errcode) 1005 *errcode = NIS_NOMEMORY; 1006 return (NULL); 1007 } 1008 1009 hostinfo->ep.ep_len = num_ep; 1010 hostinfo->ep.ep_val = addr; 1011 1012 hostinfo->name = (host) ? 1013 strdup(hostnetname) : strdup(nis_local_host()); 1014 if (hostinfo->name == NULL) { 1015 __free_nis_server(hostinfo); 1016 if (errcode) 1017 *errcode = NIS_NOMEMORY; 1018 return (NULL); 1019 } 1020 1021 if (addpubkey) { 1022 if (!host2netname(netname, hostinfo->name, NULL)) 1023 goto nocred; 1024 1025 if (mechlist = __nis_get_mechanisms(0)) { 1026 bool_t got192 = FALSE, gotothers = FALSE; 1027 extdhkey_t *keylist = NULL; 1028 size_t keylistsize = 0; 1029 int i; 1030 1031 for (i = 0; mechlist[i]; i++) { 1032 size_t binlen, binpadlen, hexkeylen, 1033 keyoffset; 1034 char *hexkey, *entryoffset; 1035 extdhkey_t *curentry, *oldkeylist; 1036 keylen_t keylen = mechlist[i]->keylen; 1037 algtype_t algtype = mechlist[i]->algtype; 1038 1039 binlen = (keylen + 7) / 8; 1040 binpadlen = ((binlen + 3) / 4) * 4; 1041 hexkeylen = binlen * 2 + 1; 1042 1043 if (!(hexkey = malloc(hexkeylen))) { 1044 __nis_release_mechanisms(mechlist); 1045 __free_nis_server(hostinfo); 1046 free(keylist); 1047 if (errcode) 1048 *errcode = NIS_NOMEMORY; 1049 return (NULL); 1050 } 1051 1052 if (getpublickey_g(netname, keylen, algtype, 1053 hexkey, 1054 hexkeylen) == 0) { 1055 free(hexkey); 1056 continue; 1057 } else { 1058 if (keylen == 192) 1059 got192 = TRUE; 1060 else 1061 gotothers = TRUE; 1062 } 1063 1064 keyoffset = keylistsize; 1065 keylistsize += sizeof (ushort_t) * 2 + 1066 binpadlen; 1067 oldkeylist = keylist; 1068 if (!(keylist = realloc(keylist, 1069 keylistsize))) { 1070 free(oldkeylist); 1071 free(hexkey); 1072 __nis_release_mechanisms(mechlist); 1073 __free_nis_server(hostinfo); 1074 if (errcode) 1075 *errcode = NIS_NOMEMORY; 1076 return (NULL); 1077 } 1078 1079 entryoffset = (char *)keylist + keyoffset; 1080 /* LINTED pointer cast */ 1081 curentry = (extdhkey_t *)entryoffset; 1082 1083 curentry->keylen = htons(keylen); 1084 curentry->algtype = htons(algtype); 1085 hex2bin(binlen, hexkey, (char *)curentry->key); 1086 1087 free(hexkey); 1088 } 1089 __nis_release_mechanisms(mechlist); 1090 1091 /* 1092 * If there is only keys for DH192, then we pretend 1093 * that DHEXT doesn't exist. 1094 * 1095 * If no keys are returned, then we no nuthin'. 1096 */ 1097 if (!gotothers) { 1098 free(keylist); 1099 if (got192) 1100 goto only192; 1101 else 1102 goto nocred; 1103 } 1104 1105 hostinfo->key_type = NIS_PK_DHEXT; 1106 hostinfo->pkey.n_len = (ushort_t)keylistsize; 1107 hostinfo->pkey.n_bytes = (char *)keylist; 1108 } else { 1109 if (getpublickey(netname, pkey)) { 1110 only192: hostinfo->key_type = NIS_PK_DH; 1111 hostinfo->pkey.n_len = strlen(pkey)+1; 1112 hostinfo->pkey.n_bytes = (char *)strdup(pkey); 1113 if (hostinfo->pkey.n_bytes == NULL) { 1114 __free_nis_server(hostinfo); 1115 if (errcode) 1116 *errcode = NIS_NOMEMORY; 1117 return (NULL); 1118 } 1119 } else { 1120 nocred: hostinfo->key_type = NIS_PK_NONE; 1121 hostinfo->pkey.n_bytes = NULL; 1122 hostinfo->pkey.n_len = 0; 1123 } 1124 } 1125 } else 1126 goto nocred; 1127 1128 return (hostinfo); 1129 } 1130 1131 1132 /* 1133 * Extract a public key given a key length and alg. type from a packed 1134 * netobj containing extended Diffie-Hellman keys. 1135 */ 1136 char * 1137 __nis_dhext_extract_pkey(netobj *no, keylen_t keylen, algtype_t algtype) 1138 { 1139 char *hexkey; 1140 /* LINTED pointer cast */ 1141 extdhkey_t *keyent = (extdhkey_t *)no->n_bytes; 1142 1143 /* LINTED pointer cast */ 1144 while (keyent < (extdhkey_t *)(no->n_bytes + no->n_len)) { 1145 char *keyoffset; 1146 size_t binlen = (ntohs(keyent->keylen) + 7) / 8; 1147 size_t binpadlen = ((binlen + 3) / 4) * 4; 1148 size_t hexkeylen = binlen * 2 + 1; 1149 1150 if (keylen == ntohs(keyent->keylen) && 1151 algtype == ntohs(keyent->algtype)) { 1152 1153 if (!(hexkey = malloc(hexkeylen))) 1154 return (NULL); 1155 1156 (void) bin2hex(binlen, keyent->key, hexkey); 1157 return (hexkey); 1158 } 1159 keyoffset = (char *)keyent + (sizeof (ushort_t) * 2) + 1160 binpadlen; 1161 /* LINTED pointer cast */ 1162 keyent = (extdhkey_t *)keyoffset; 1163 } 1164 return (NULL); 1165 } 1166 1167 1168 /* 1169 * Returns a list of key lengths and alg. types for a given nis_server 1170 * structure. 1171 */ 1172 int 1173 __nis_dhext_extract_keyinfo(nis_server *ns, extdhkey_t **retdat) 1174 { 1175 extdhkey_t *keyinfolist = NULL, *tmplist = NULL; 1176 int count = 0; 1177 /* LINTED pointer cast */ 1178 extdhkey_t *keyent = (extdhkey_t *)ns->pkey.n_bytes; 1179 1180 switch (ns->key_type) { 1181 case NIS_PK_DH: 1182 if (!(keyinfolist = malloc(sizeof (extdhkey_t)))) 1183 return (0); 1184 keyinfolist[0].keylen = 192; 1185 keyinfolist[0].algtype = 0; 1186 1187 *retdat = keyinfolist; 1188 return (1); 1189 1190 case NIS_PK_DHEXT: 1191 /* LINTED pointer cast */ 1192 while (keyent < (extdhkey_t *)(ns->pkey.n_bytes + 1193 ns->pkey.n_len)) { 1194 size_t binlen = (keyent->keylen + 7) / 8; 1195 size_t binpadlen = ((binlen + 3) / 4) * 4; 1196 char *keyoffset; 1197 1198 tmplist = keyinfolist; 1199 1200 if (!(keyinfolist = realloc(keyinfolist, 1201 (count + 1) * 1202 sizeof (extdhkey_t)))) { 1203 free(tmplist); 1204 return (0); 1205 } 1206 keyinfolist[count].keylen = ntohs(keyent->keylen); 1207 keyinfolist[count].algtype = ntohs(keyent->algtype); 1208 1209 keyoffset = (char *)keyent + (sizeof (ushort_t) * 2) + 1210 binpadlen; 1211 /* LINTED pointer cast */ 1212 keyent = (extdhkey_t *)keyoffset; 1213 count++; 1214 } 1215 *retdat = keyinfolist; 1216 return (count); 1217 1218 default: 1219 return (0); 1220 } 1221 } 1222