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