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 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 26 /* All Rights Reserved */ 27 /* 28 * Portions of this source code were derived from Berkeley 29 * 4.3 BSD under license from the Regents of the University of 30 * California. 31 */ 32 /* 33 * ==== hack-attack: possibly MT-safe but definitely not MT-hot. 34 * ==== turn this into a real switch frontend and backends 35 * 36 * Well, at least the API doesn't involve pointers-to-static. 37 */ 38 39 #pragma ident "%Z%%M% %I% %E% SMI" 40 41 /* 42 * netname utility routines (getnetname, user2netname, host2netname). 43 * 44 * Convert from unix names (uid, gid) to network wide names. 45 * This module is operating system dependent! 46 * What we define here will work with any unix system that has adopted 47 * the Sun NIS domain architecture. 48 */ 49 50 #undef NIS 51 52 #include "mt.h" 53 #include "rpc_mt.h" 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <unistd.h> 57 #include <sys/types.h> 58 #include <rpc/trace.h> 59 #include <ctype.h> 60 #include <string.h> 61 #include <syslog.h> 62 #include <sys/param.h> 63 #include <rpc/rpc.h> 64 #include <rpcsvc/nis.h> 65 #include <rpcsvc/nis_dhext.h> 66 #include <nsswitch.h> 67 #include <syslog.h> 68 69 #ifndef MAXHOSTNAMELEN 70 #define MAXHOSTNAMELEN 256 71 #endif 72 #ifndef NGROUPS 73 #define NGROUPS 16 74 #endif 75 76 /* 77 * the value for NOBODY_UID is set by the SVID. The following define also 78 * appears in netnamer.c 79 */ 80 81 #define NOBODY_UID 60001 82 83 extern int __nis_principal(); 84 extern int getdomainname(); 85 extern char *strncpy(); 86 extern int key_call(); 87 #define OPSYS_LEN 4 88 #define PKTABLE_LEN 12 89 static const char *OPSYS = "unix"; 90 static const char *PKTABLE = "cred.org_dir"; 91 92 93 /* 94 * default publickey policy: 95 * publickey: nis [NOTFOUND = return] files 96 */ 97 98 99 /* NSW_NOTSUCCESS NSW_NOTFOUND NSW_UNAVAIL NSW_TRYAGAIN */ 100 #define DEF_ACTION {__NSW_RETURN, __NSW_RETURN, __NSW_CONTINUE, __NSW_CONTINUE} 101 102 static struct __nsw_lookup lookup_files = {"files", DEF_ACTION, NULL, NULL}, 103 lookup_nis = {"nis", DEF_ACTION, NULL, &lookup_files}; 104 static struct __nsw_switchconfig publickey_default = 105 {0, "publickey", 2, &lookup_nis}; 106 107 static mutex_t serialize_netname = DEFAULTMUTEX; 108 109 /* 110 * Convert unix cred to network-name using nisplus 111 * nisplus cred table has the following format: 112 * 113 * cname auth_type auth_name public private 114 * ---------------------------------------------------------- 115 * nisname DES netname pubkey private_key 116 * nisname LOCAL uid gidlist 117 * 118 * Obtain netname given <uid,domain>. 119 * 0. If domain is NULL (indicating local domain), first try to get 120 * netname from keyserv (keylogin sets this). 121 * 1. Get the nisplus principal name from the LOCAL entry of the cred 122 * table in the specified domain (the local domain if domain is NULL). 123 * 2. Using the principal name, lookup the DES entry and extract netname. 124 */ 125 126 static int 127 user2netname_nisplus(err, netname, uid, domain) 128 int *err; 129 char netname[MAXNETNAMELEN + 1]; 130 uid_t uid; 131 char *domain; 132 { 133 key_netstres kres; 134 nis_result *nres; 135 int len; 136 uid_t my_uid; 137 char principal[NIS_MAXNAMELEN+1]; 138 char buf[NIS_MAXNAMELEN+1]; 139 int status; 140 mechanism_t **mechs; 141 char auth_type[MECH_MAXATNAME+1]; 142 143 trace1(TR_user2netname_nisplus, 0); 144 145 my_uid = geteuid(); 146 147 if (my_uid == uid && domain == NULL) { 148 /* 149 * Look up the keyserv interface routines to see if 150 * netname is stored there. 151 */ 152 kres.key_netstres_u.knet.st_netname = NULL; 153 if (key_call((rpcproc_t)KEY_NET_GET, xdr_void, (char *)NULL, 154 xdr_key_netstres, (char *)&kres) && 155 kres.status == KEY_SUCCESS) { 156 len = strlen(kres.key_netstres_u.knet.st_netname); 157 (void) strncpy(netname, 158 kres.key_netstres_u.knet.st_netname, 159 len +1); 160 free(kres.key_netstres_u.knet.st_netname); 161 trace1(TR_user2netname_nisplus, 1); 162 *err = __NSW_SUCCESS; 163 return (1); 164 } 165 } 166 167 168 /* 169 * 1. Determine user's nis+ principal name. 170 * 171 * If domain is specified, we want to look up the uid in the 172 * specified domain to determine the user's principal name. 173 * Otherwise, get principal name from local directory. 174 */ 175 if (domain == NULL) 176 domain = nis_local_directory(); 177 /* 178 * Don't use nis_local_principal here because we want to 179 * catch the TRYAGAIN case so that we handle it properly. 180 */ 181 status = __nis_principal(principal, uid, domain); 182 183 if (status != NIS_SUCCESS && status != NIS_S_SUCCESS) { 184 switch (status) { 185 case NIS_NOTFOUND: 186 case NIS_PARTIAL: 187 case NIS_NOSUCHNAME: 188 case NIS_NOSUCHTABLE: 189 *err = __NSW_NOTFOUND; 190 break; 191 case NIS_S_NOTFOUND: 192 case NIS_TRYAGAIN: 193 *err = __NSW_TRYAGAIN; 194 syslog(LOG_ERR, 195 "user2netname: (nis+ lookup): %s\n", 196 nis_sperrno(status)); 197 break; 198 default: 199 *err = __NSW_UNAVAIL; 200 syslog(LOG_ERR, 201 "user2netname: (nis+ lookup): %s\n", 202 nis_sperrno(status)); 203 } 204 205 trace1(TR_user2netname_nisplus, 1); 206 return (0); 207 } 208 209 /* 210 * 2. use nis+ principal name to get netname by getting a PK entry. 211 * 212 * (Use NOAUTH to prevent recursion.) 213 */ 214 domain = nis_domain_of(principal); 215 if ((strlen(principal)+strlen(domain)+PKTABLE_LEN+ 28) > 216 (size_t)NIS_MAXNAMELEN) { 217 *err = __NSW_UNAVAIL; 218 trace1(TR_user2netname_nisplus, 1); 219 return (0); 220 } 221 222 if (mechs = __nis_get_mechanisms(FALSE)) { 223 mechanism_t **mpp; 224 225 /* 226 * Loop thru mechanism types till we find one in the 227 * cred table for this user. 228 */ 229 for (mpp = mechs; *mpp; mpp++) { 230 mechanism_t *mp = *mpp; 231 232 if (AUTH_DES_COMPAT_CHK(mp)) { 233 __nis_release_mechanisms(mechs); 234 goto try_auth_des; 235 } 236 if (! VALID_MECH_ENTRY(mp)) 237 continue; 238 239 if (! __nis_mechalias2authtype(mp->alias, auth_type, 240 sizeof (auth_type))) 241 continue; 242 243 (void) sprintf(buf, 244 "[cname=\"%s\",auth_type=\"%s\"],%s.%s", 245 principal, auth_type, PKTABLE, domain); 246 if (buf[strlen(buf)-1] != '.') 247 (void) strcat(buf, "."); 248 249 nres = nis_list(buf, 250 USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH, 251 NULL, NULL); 252 253 /* 254 * If the entry is not found, let's try the next one, 255 * else it's success or a serious enough NIS+ err 256 * to bail on. 257 */ 258 if (nres->status != NIS_NOTFOUND) 259 break; 260 } 261 } else { 262 try_auth_des: 263 /* 264 * No valid mechs exist or the AUTH_DES compat entry was 265 * found in the security cf. 266 */ 267 (void) sprintf(buf, "[cname=\"%s\",auth_type=DES],%s.%s", 268 principal, PKTABLE, domain); 269 if (buf[strlen(buf)-1] != '.') 270 (void) strcat(buf, "."); 271 272 nres = nis_list(buf, 273 USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH, 274 NULL, NULL); 275 } 276 277 switch (nres->status) { 278 case NIS_SUCCESS: 279 case NIS_S_SUCCESS: 280 break; /* go and do something useful */ 281 case NIS_NOTFOUND: 282 case NIS_PARTIAL: 283 case NIS_NOSUCHNAME: 284 case NIS_NOSUCHTABLE: 285 *err = __NSW_NOTFOUND; 286 nis_freeresult(nres); 287 trace1(TR_user2netname_nisplus, 1); 288 return (0); 289 case NIS_S_NOTFOUND: 290 case NIS_TRYAGAIN: 291 *err = __NSW_TRYAGAIN; 292 syslog(LOG_ERR, 293 "user2netname: (nis+ lookup): %s\n", 294 nis_sperrno(nres->status)); 295 nis_freeresult(nres); 296 trace1(TR_user2netname_nisplus, 1); 297 return (0); 298 default: 299 *err = __NSW_UNAVAIL; 300 syslog(LOG_ERR, "user2netname: (nis+ lookup): %s\n", 301 nis_sperrno(nres->status)); 302 nis_freeresult(nres); 303 trace1(TR_user2netname_nisplus, 1); 304 return (0); 305 } 306 307 if (nres->objects.objects_len > 1) { 308 /* 309 * Principal with more than one entry for this mech type? 310 * Something wrong with cred table. Should be unique. 311 * Warn user and continue. 312 */ 313 syslog(LOG_ALERT, 314 "user2netname: %s entry for %s not unique", 315 auth_type, principal); 316 } 317 318 len = ENTRY_LEN(nres->objects.objects_val, 2); 319 if (len > MAXNETNAMELEN) { 320 *err = __NSW_UNAVAIL; 321 syslog(LOG_ERR, "user2netname: netname of '%s' too long", 322 principal); 323 nis_freeresult(nres); 324 trace1(TR_user2netname_nisplus, 1); 325 return (0); 326 } 327 (void) strncpy(netname, ENTRY_VAL(nres->objects.objects_val, 2), len); 328 netname[len] = '\0'; 329 nis_freeresult(nres); 330 *err = __NSW_SUCCESS; 331 trace1(TR_user2netname_nisplus, 1); 332 return (1); 333 } 334 335 #define MAXIPRINT (11) /* max length of printed integer */ 336 337 /* 338 * Convert unix cred to network-name by concatenating the 339 * 3 pieces of information <opsys type> <uid> <domain>. 340 */ 341 342 static int 343 user2netname_nis(err, netname, uid, domain) 344 int *err; 345 char netname[MAXNETNAMELEN + 1]; 346 uid_t uid; 347 char *domain; 348 { 349 int i; 350 char *dfltdom; 351 if (domain == NULL) { 352 if (__rpc_get_default_domain(&dfltdom) != 0) { 353 trace1(TR_user2netname_nis, 1); 354 *err = __NSW_UNAVAIL; 355 return (0); 356 } 357 domain = dfltdom; 358 } 359 if ((strlen(domain) + OPSYS_LEN + 3 + MAXIPRINT) > 360 (size_t)MAXNETNAMELEN) { 361 trace1(TR_user2netname_nis, 1); 362 *err = __NSW_UNAVAIL; 363 return (0); 364 } 365 (void) sprintf(netname, "%s.%d@%s", OPSYS, (int)uid, domain); 366 i = strlen(netname); 367 if (netname[i-1] == '.') 368 netname[i-1] = '\0'; 369 trace1(TR_user2netname_nis, 1); 370 *err = __NSW_SUCCESS; 371 return (1); 372 } 373 374 /* 375 * Figure out my fully qualified network name 376 */ 377 int 378 getnetname(name) 379 char name[MAXNETNAMELEN + 1]; 380 { 381 uid_t uid; 382 int dummy; 383 384 trace1(TR_getnetname, 0); 385 386 uid = geteuid(); 387 if (uid == 0) 388 dummy = host2netname(name, (char *)NULL, (char *)NULL); 389 else 390 dummy = user2netname(name, uid, (char *)NULL); 391 392 trace1(TR_getnetname, 1); 393 return (dummy); 394 } 395 396 397 /* 398 * Figure out the fully qualified network name for the given uid. 399 * This is a private interface. 400 */ 401 int 402 __getnetnamebyuid(name, uid) 403 char name[MAXNETNAMELEN + 1]; 404 uid_t uid; 405 { 406 int dummy; 407 408 trace1(TR___getnetnamebyuid, 0); 409 410 if (uid == 0) 411 dummy = host2netname(name, (char *)NULL, (char *)NULL); 412 else 413 dummy = user2netname(name, uid, (char *)NULL); 414 415 trace1(TR___getnetnamebyuid, 1); 416 return (dummy); 417 } 418 419 /* 420 * Convert unix cred to network-name 421 * 422 * It uses the publickey policy in the /etc/nsswitch.conf file 423 * (Unless the netname is "nobody", which is special cased). 424 * If there is no publickey policy in /etc/nsswitch.conf, 425 * the default publickey policy is used, which is 426 * publickey: nis [NOTFOUND=return] files 427 * Note that for the non-nisplus case, there is no failover 428 * so only the first entry would be relevant for those cases. 429 */ 430 int 431 user2netname(netname, uid, domain) 432 char netname[MAXNETNAMELEN + 1]; 433 const uid_t uid; 434 const char *domain; 435 { 436 struct __nsw_switchconfig *conf; 437 struct __nsw_lookup *look; 438 int needfree = 1, res = 0; 439 enum __nsw_parse_err perr; 440 int err; 441 442 trace1(TR_user2netname, 0); 443 444 /* 445 * Take care of the special case of "nobody". If the uid is 446 * the value assigned by the SVID for nobody, return the string 447 * "nobody". 448 */ 449 450 if (uid == NOBODY_UID) { 451 (void) strcpy(netname, "nobody"); 452 return (1); 453 } 454 455 netname[0] = '\0'; /* make null first (no need for memset) */ 456 457 mutex_lock(&serialize_netname); 458 459 conf = __nsw_getconfig("publickey", &perr); 460 if (! conf) { 461 conf = &publickey_default; 462 needfree = 0; 463 } 464 465 for (look = conf->lookups; look; look = look->next) { 466 if (strcmp(look->service_name, "nisplus") == 0) 467 res = user2netname_nisplus(&err, 468 netname, uid, (char *)domain); 469 /* ldap, nis, and files all do the same thing. */ 470 else if (strcmp(look->service_name, "ldap") == 0 || 471 strcmp(look->service_name, "nis") == 0 || 472 strcmp(look->service_name, "files") == 0) 473 res = user2netname_nis(&err, 474 netname, uid, (char *)domain); 475 else { 476 syslog(LOG_INFO, 477 "user2netname: unknown nameservice \ 478 for publickey info '%s'\n", 479 look->service_name); 480 err = __NSW_UNAVAIL; 481 } 482 switch (look->actions[err]) { 483 case __NSW_CONTINUE : 484 break; 485 case __NSW_RETURN : 486 if (needfree) 487 __nsw_freeconfig(conf); 488 mutex_unlock(&serialize_netname); 489 trace1(TR_user2netname, 1); 490 return (res); 491 default : 492 syslog(LOG_ERR, 493 "user2netname: Unknown action for nameservice '%s'", 494 look->service_name); 495 } 496 } 497 if (needfree) 498 __nsw_freeconfig(conf); 499 mutex_unlock(&serialize_netname); 500 trace1(TR_user2netname, 1); 501 return (0); 502 } 503 504 505 /* 506 * Convert host to network-name 507 * This routine returns following netnames given the host and domain 508 * arguments defined below: (domainname=y.z) 509 * Arguments 510 * host domain netname 511 * ---- ------ ------- 512 * - - unix.m@y.z (hostname=m) 513 * - a.b unix.m@a.b (hostname=m) 514 * - - unix.m@y.z (hostname=m.w.x) 515 * - a.b unix.m@a.b (hostname=m.w.x) 516 * h - unix.h@y.z 517 * h a.b unix.h@a.b 518 * h.w.x - unix.h@w.x 519 * h.w.x a.b unix.h@a.b 520 */ 521 int 522 host2netname(netname, host, domain) 523 char netname[MAXNETNAMELEN + 1]; 524 const char *host; 525 const char *domain; 526 { 527 char *p; 528 char hostname[MAXHOSTNAMELEN + 1]; 529 char domainname[MAXHOSTNAMELEN + 1]; 530 char *dot_in_host; 531 int i; 532 size_t len; 533 534 trace1(TR_host2netname, 0); 535 536 netname[0] = '\0'; /* make null first (no need for memset) */ 537 538 if (host == NULL) { 539 (void) strncpy(hostname, nis_local_host(), sizeof (hostname)); 540 p = (char *)strchr(hostname, '.'); 541 if (p) { 542 *p++ = '\0'; 543 /* if no domain passed, use tail of nis_local_host() */ 544 if (domain == NULL) { 545 domain = p; 546 } 547 } 548 } else { 549 len = strlen(host); 550 if (len >= sizeof (hostname)) { 551 return (0); 552 } 553 (void) strcpy(hostname, host); 554 } 555 556 dot_in_host = (char *)strchr(hostname, '.'); 557 if (domain == NULL) { 558 p = dot_in_host; 559 if (p) { 560 p = (char *)nis_domain_of(hostname); 561 len = strlen(p); 562 if (len >= sizeof (domainname)) { 563 return (0); 564 } 565 (void) strcpy(domainname, p); 566 } else { 567 domainname[0] = NULL; 568 if (getdomainname(domainname, MAXHOSTNAMELEN) < 0) 569 return (0); 570 } 571 } else { 572 len = strlen(domain); 573 if (len >= sizeof (domainname)) { 574 return (0); 575 } 576 (void) strcpy(domainname, domain); 577 } 578 579 i = strlen(domainname); 580 if (i == 0) 581 /* No domainname */ 582 return (0); 583 if (domainname[i - 1] == '.') 584 domainname[i - 1] = 0; 585 586 if (dot_in_host) { /* strip off rest of name */ 587 *dot_in_host = '\0'; 588 } 589 590 if ((strlen(domainname) + strlen(hostname) + OPSYS_LEN + 3) 591 > (size_t)MAXNETNAMELEN) { 592 trace1(TR_host2netname, 1); 593 return (0); 594 } 595 596 (void) sprintf(netname, "%s.%s@%s", OPSYS, hostname, domainname); 597 trace1(TR_host2netname, 1); 598 return (1); 599 } 600