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 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 /* 30 * Portions of this source code were derived from Berkeley 31 * 4.3 BSD under license from the Regents of the University of 32 * California. 33 */ 34 /* 35 * ==== hack-attack: possibly MT-safe but definitely not MT-hot. 36 * ==== turn this into a real switch frontend and backends 37 * 38 * Well, at least the API doesn't involve pointers-to-static. 39 */ 40 41 #pragma ident "%Z%%M% %I% %E% SMI" 42 43 /* 44 * netname utility routines (getnetname, user2netname, host2netname). 45 * 46 * Convert from unix names (uid, gid) to network wide names. 47 * This module is operating system dependent! 48 * What we define here will work with any unix system that has adopted 49 * the Sun NIS domain architecture. 50 */ 51 52 #undef NIS 53 54 #include "mt.h" 55 #include "rpc_mt.h" 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <unistd.h> 59 #include <sys/types.h> 60 #include <ctype.h> 61 #include <string.h> 62 #include <syslog.h> 63 #include <sys/param.h> 64 #include <rpc/rpc.h> 65 #include <rpcsvc/nis.h> 66 #include <rpcsvc/nis_dhext.h> 67 #include <nsswitch.h> 68 #include <syslog.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 = 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(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) strcpy(netname, "nobody"); 413 return (1); 414 } 415 416 netname[0] = '\0'; /* make null first (no need for memset) */ 417 418 (void) mutex_lock(&serialize_netname); 419 420 conf = __nsw_getconfig("publickey", &perr); 421 if (!conf) { 422 conf = &publickey_default; 423 needfree = 0; 424 } 425 426 for (look = conf->lookups; look; look = look->next) { 427 if (strcmp(look->service_name, "nisplus") == 0) 428 res = user2netname_nisplus(&err, 429 netname, uid, (char *)domain); 430 /* ldap, nis, and files all do the same thing. */ 431 else if (strcmp(look->service_name, "ldap") == 0 || 432 strcmp(look->service_name, "nis") == 0 || 433 strcmp(look->service_name, "files") == 0) 434 res = user2netname_nis(&err, 435 netname, uid, (char *)domain); 436 else { 437 syslog(LOG_INFO, 438 "user2netname: unknown nameservice \ 439 for publickey info '%s'\n", 440 look->service_name); 441 err = __NSW_UNAVAIL; 442 } 443 switch (look->actions[err]) { 444 case __NSW_CONTINUE : 445 break; 446 case __NSW_RETURN : 447 if (needfree) 448 __nsw_freeconfig(conf); 449 (void) mutex_unlock(&serialize_netname); 450 return (res); 451 default : 452 syslog(LOG_ERR, 453 "user2netname: Unknown action for nameservice '%s'", 454 look->service_name); 455 } 456 } 457 if (needfree) 458 __nsw_freeconfig(conf); 459 (void) mutex_unlock(&serialize_netname); 460 return (0); 461 } 462 463 464 /* 465 * Convert host to network-name 466 * This routine returns following netnames given the host and domain 467 * arguments defined below: (domainname=y.z) 468 * Arguments 469 * host domain netname 470 * ---- ------ ------- 471 * - - unix.m@y.z (hostname=m) 472 * - a.b unix.m@a.b (hostname=m) 473 * - - unix.m@y.z (hostname=m.w.x) 474 * - a.b unix.m@a.b (hostname=m.w.x) 475 * h - unix.h@y.z 476 * h a.b unix.h@a.b 477 * h.w.x - unix.h@w.x 478 * h.w.x a.b unix.h@a.b 479 */ 480 int 481 host2netname(char netname[MAXNETNAMELEN + 1], const char *host, 482 const char *domain) 483 { 484 char *p; 485 char hostname[MAXHOSTNAMELEN + 1]; 486 char domainname[MAXHOSTNAMELEN + 1]; 487 char *dot_in_host; 488 int i; 489 size_t len; 490 491 netname[0] = '\0'; /* make null first (no need for memset) */ 492 493 if (host == NULL) { 494 (void) strncpy(hostname, nis_local_host(), sizeof (hostname)); 495 p = (char *)strchr(hostname, '.'); 496 if (p) { 497 *p++ = '\0'; 498 /* if no domain passed, use tail of nis_local_host() */ 499 if (domain == NULL) { 500 domain = p; 501 } 502 } 503 } else { 504 len = strlen(host); 505 if (len >= sizeof (hostname)) { 506 return (0); 507 } 508 (void) strcpy(hostname, host); 509 } 510 511 dot_in_host = (char *)strchr(hostname, '.'); 512 if (domain == NULL) { 513 p = dot_in_host; 514 if (p) { 515 p = (char *)nis_domain_of(hostname); 516 len = strlen(p); 517 if (len >= sizeof (domainname)) { 518 return (0); 519 } 520 (void) strcpy(domainname, p); 521 } else { 522 domainname[0] = NULL; 523 if (getdomainname(domainname, MAXHOSTNAMELEN) < 0) 524 return (0); 525 } 526 } else { 527 len = strlen(domain); 528 if (len >= sizeof (domainname)) { 529 return (0); 530 } 531 (void) strcpy(domainname, domain); 532 } 533 534 i = strlen(domainname); 535 if (i == 0) 536 /* No domainname */ 537 return (0); 538 if (domainname[i - 1] == '.') 539 domainname[i - 1] = 0; 540 541 if (dot_in_host) { /* strip off rest of name */ 542 *dot_in_host = '\0'; 543 } 544 545 if ((strlen(domainname) + strlen(hostname) + OPSYS_LEN + 3) 546 > (size_t)MAXNETNAMELEN) { 547 return (0); 548 } 549 550 (void) snprintf(netname, MAXNETNAMELEN + 1, 551 "%s.%s@%s", OPSYS, hostname, domainname); 552 return (1); 553 } 554