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