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 2005 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 convert from netnames to unix names (uid, gid) 43 * 44 * This module is operating system dependent! 45 * What we define here will work with any unix system that has adopted 46 * the Sun NIS domain architecture. 47 */ 48 49 #undef NIS 50 #include "mt.h" 51 #include "rpc_mt.h" 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <sys/types.h> 55 #include <rpc/trace.h> 56 #include <ctype.h> 57 #include <grp.h> 58 #include <pwd.h> 59 #include <string.h> 60 #include <syslog.h> 61 #include <sys/param.h> 62 #include <nsswitch.h> 63 #include <rpc/rpc.h> 64 #include <rpcsvc/nis.h> 65 #include <rpcsvc/ypclnt.h> 66 #include "nsl_stdio_prv.h" 67 #include <nss_dbdefs.h> 68 69 static const char OPSYS[] = "unix"; 70 static const char NETIDFILE[] = "/etc/netid"; 71 static const char NETID[] = "netid.byname"; 72 static const char PKTABLE[] = "cred.org_dir"; 73 #define PKTABLE_LEN 12 74 #define OPSYS_LEN 4 75 76 #ifndef NGROUPS 77 #define NGROUPS 16 78 #endif 79 80 extern int _getgroupsbymember(const char *, gid_t[], int, int); 81 82 /* 83 * the value for NOBODY_UID is set by the SVID. The following define also 84 * appears in netname.c 85 */ 86 87 #define NOBODY_UID 60001 88 89 /* 90 * default publickey policy: 91 * publickey: nis [NOTFOUND = return] files 92 */ 93 94 95 /* NSW_NOTSUCCESS NSW_NOTFOUND NSW_UNAVAIL NSW_TRYAGAIN */ 96 #define DEF_ACTION {__NSW_RETURN, __NSW_RETURN, __NSW_CONTINUE, __NSW_CONTINUE} 97 98 static struct __nsw_lookup lookup_files = {"files", DEF_ACTION, NULL, NULL}, 99 lookup_nis = {"nis", DEF_ACTION, NULL, &lookup_files}; 100 static struct __nsw_switchconfig publickey_default = 101 {0, "publickey", 2, &lookup_nis}; 102 103 static mutex_t serialize_netname_r = DEFAULTMUTEX; 104 105 struct netid_userdata { 106 uid_t *uidp; 107 gid_t *gidp; 108 int *gidlenp; 109 gid_t *gidlist; 110 }; 111 112 static int 113 parse_uid(s, argp) 114 char *s; 115 struct netid_userdata *argp; 116 { 117 uid_t u; 118 119 trace1(TR_parse_uid, 0); 120 121 if (!s || !isdigit(*s)) { 122 syslog(LOG_ERR, 123 "netname2user: expecting uid '%s'", s); 124 trace1(TR_parse_uid, 1); 125 return (__NSW_NOTFOUND); /* xxx need a better error */ 126 } 127 128 /* Fetch the uid */ 129 u = (uid_t)(atoi(s)); 130 131 if (u == 0) { 132 syslog(LOG_ERR, "netname2user: should not have uid 0"); 133 trace1(TR_parse_uid, 1); 134 return (__NSW_NOTFOUND); 135 } 136 *(argp->uidp) = u; 137 trace1(TR_parse_uid, 1); 138 return (__NSW_SUCCESS); 139 } 140 141 142 /* parse a comma separated gid list */ 143 static int 144 parse_gidlist(p, argp) 145 char *p; 146 struct netid_userdata *argp; 147 { 148 int len; 149 gid_t g; 150 151 trace1(TR_parse_gidlist, 0); 152 if (! p || (! isdigit(*p))) { 153 syslog(LOG_ERR, 154 "netname2user: missing group id list in '%s'.", 155 p); 156 trace1(TR_parse_gidlist, 1); 157 return (__NSW_NOTFOUND); 158 } 159 160 g = (gid_t)(atoi(p)); 161 *(argp->gidp) = g; 162 163 len = 0; 164 while (p = strchr(p, ',')) 165 argp->gidlist[len++] = (gid_t)atoi(++p); 166 *(argp->gidlenp) = len; 167 trace1(TR_parse_gidlist, 1); 168 return (__NSW_SUCCESS); 169 } 170 171 172 /* 173 * parse_netid_str() 174 * 175 * Parse uid and group information from the passed string. 176 * 177 * The format of the string passed is 178 * uid:gid,grp,grp, ... 179 * 180 */ 181 static int 182 parse_netid_str(s, argp) 183 char *s; 184 struct netid_userdata *argp; 185 { 186 char *p; 187 int err; 188 189 trace1(TR_parse_netid_str, 0); 190 191 /* get uid */ 192 err = parse_uid(s, argp); 193 if (err != __NSW_SUCCESS) { 194 trace1(TR_parse_netid_str, 1); 195 return (err); 196 } 197 198 199 /* Now get the group list */ 200 p = strchr(s, ':'); 201 if (!p) { 202 syslog(LOG_ERR, 203 "netname2user: missing group id list in '%s'", 204 s); 205 trace1(TR_parse_netid_str, 1); 206 return (__NSW_NOTFOUND); 207 } 208 ++p; /* skip ':' */ 209 err = parse_gidlist(p, argp); 210 trace1(TR_parse_netid_str, 1); 211 return (err); 212 } 213 214 static int 215 parse_uid_gidlist(ustr, gstr, argp) 216 char *ustr; 217 char *gstr; 218 struct netid_userdata *argp; 219 { 220 int err; 221 222 trace1(TR_parse_uid_gidlist, 0); 223 224 /* get uid */ 225 err = parse_uid(ustr, argp); 226 if (err != __NSW_SUCCESS) { 227 trace1(TR_parse_uid_gidlist, 1); 228 return (err); 229 } 230 231 /* Now get the group list */ 232 err = parse_gidlist(gstr, argp); 233 trace1(TR_parse_uid_gidlist, 1); 234 return (err); 235 } 236 237 238 /* 239 * netname2user_files() 240 * 241 * This routine fetches the netid information from the "files" nameservice. 242 * ie /etc/netid. 243 */ 244 static int 245 netname2user_files(err, netname, argp) 246 int *err; 247 char *netname; 248 struct netid_userdata *argp; 249 { 250 char buf[512]; /* one line from the file */ 251 char *name; 252 char *value; 253 char *res; 254 __NSL_FILE *fd; 255 256 trace1(TR_netname2user_files, 0); 257 fd = __nsl_fopen(NETIDFILE, "r"); 258 if (fd == (__NSL_FILE *)0) { 259 *err = __NSW_UNAVAIL; 260 trace1(TR_netname2user_files, 1); 261 return (0); 262 } 263 /* 264 * for each line in the file parse it appropriately 265 * file format is : 266 * netid uid:grp,grp,grp # for users 267 * netid 0:hostname # for hosts 268 */ 269 while (! __nsl_feof(fd)) { 270 res = __nsl_fgets(buf, 512, fd); 271 if (res == NULL) 272 break; 273 274 /* Skip comments and blank lines */ 275 if ((*res == '#') || (*res == '\n')) 276 continue; 277 278 name = &(buf[0]); 279 while (isspace(*name)) 280 name++; 281 if (*name == '\0') /* blank line continue */ 282 continue; 283 value = name; /* will contain the value eventually */ 284 while (! isspace(*value)) 285 value++; 286 if (*value == '\0') { 287 syslog(LOG_WARNING, 288 "netname2user: badly formatted line in %s.", 289 NETIDFILE); 290 continue; 291 } 292 *value++ = '\0'; /* nul terminate the name */ 293 294 if (strcasecmp(name, netname) == 0) { 295 __nsl_fclose(fd); 296 while (isspace(*value)) 297 value++; 298 *err = parse_netid_str(value, argp); 299 trace1(TR_netname2user_files, 1); 300 return (*err == __NSW_SUCCESS); 301 } 302 } 303 __nsl_fclose(fd); 304 *err = __NSW_NOTFOUND; 305 trace1(TR_netname2user_files, 1); 306 return (0); 307 } 308 309 /* 310 * netname2user_nis() 311 * 312 * This function reads the netid from the NIS (YP) nameservice. 313 */ 314 static int 315 netname2user_nis(err, netname, argp) 316 int *err; 317 char *netname; 318 struct netid_userdata *argp; 319 { 320 char *domain; 321 int yperr; 322 char *lookup; 323 int len; 324 325 trace1(TR_netname2user_nis, 0); 326 domain = strchr(netname, '@'); 327 if (! domain) { 328 *err = __NSW_UNAVAIL; 329 trace1(TR_netname2user_nis, 1); 330 return (0); 331 } 332 333 /* Point past the '@' character */ 334 domain++; 335 lookup = NULL; 336 yperr = yp_match(domain, (char *)NETID, netname, strlen(netname), 337 &lookup, &len); 338 switch (yperr) { 339 case 0: 340 break; /* the successful case */ 341 342 default : 343 /* 344 * XXX not sure about yp_match semantics. 345 * should err be set to NOTFOUND here? 346 */ 347 *err = __NSW_UNAVAIL; 348 trace1(TR_netname2user_nis, 1); 349 return (0); 350 } 351 if (lookup) { 352 lookup[len] = '\0'; 353 *err = parse_netid_str(lookup, argp); 354 free(lookup); 355 trace1(TR_netname2user_nis, 1); 356 return (*err == __NSW_SUCCESS); 357 } else { 358 trace1(TR_netname2user_nis, 1); 359 *err = __NSW_NOTFOUND; 360 return (0); 361 } 362 } 363 364 /* 365 * Obtain user information (uid, gidlist) from nisplus. 366 * What we're trying to do here is to map a netname into 367 * local unix information (uid, gids), relevant in 368 * the *local* domain. 369 * 370 * cname auth_type auth_name public private 371 * ---------------------------------------------------------- 372 * nisname DES netname pubkey prikey 373 * nisname LOCAL uid gidlist 374 * 375 * 1. Find out which 'home' domain to look for user's DES entry. 376 * This is gotten from the domain part of the netname. 377 * 2. Get the nisplus principal name from the DES entry in the cred 378 * table of user's home domain. 379 * 3. Use the nisplus principal name and search in the cred table of 380 * the *local* directory for the LOCAL entry. 381 * 382 * Note that we need this translation of netname to <uid,gidlist> to be 383 * secure, so we *must* use authenticated connections. 384 */ 385 static int 386 netname2user_nisplus(err, netname, argp) 387 int *err; 388 char *netname; 389 struct netid_userdata *argp; 390 { 391 char *domain; 392 nis_result *res; 393 char sname[NIS_MAXNAMELEN+1]; /* search criteria + table name */ 394 char principal[NIS_MAXNAMELEN+1]; 395 int len; 396 397 trace1(TR_netname2user_nisplus, 0); 398 399 400 /* 1. Get home domain of user. */ 401 domain = strchr(netname, '@'); 402 if (! domain) { 403 *err = __NSW_UNAVAIL; 404 trace1(TR_netname2user_nisplus, 1); 405 return (0); 406 } 407 domain++; /* skip '@' */ 408 409 410 /* 2. Get user's nisplus principal name. */ 411 if ((strlen(netname)+strlen(domain)+PKTABLE_LEN+32) > 412 (size_t)NIS_MAXNAMELEN) { 413 *err = __NSW_UNAVAIL; 414 trace1(TR_netname2user_nisplus, 1); 415 return (0); 416 } 417 sprintf(sname, "[auth_name=\"%s\",auth_type=DES],%s.%s", 418 netname, PKTABLE, domain); 419 if (sname[strlen(sname) - 1] != '.') 420 strcat(sname, "."); 421 422 /* must use authenticated call here */ 423 /* XXX but we cant, for now. XXX */ 424 res = nis_list(sname, USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH, 425 NULL, NULL); 426 switch (res->status) { 427 case NIS_SUCCESS: 428 case NIS_S_SUCCESS: 429 break; /* go and do something useful */ 430 case NIS_NOTFOUND: 431 case NIS_PARTIAL: 432 case NIS_NOSUCHNAME: 433 case NIS_NOSUCHTABLE: 434 *err = __NSW_NOTFOUND; 435 nis_freeresult(res); 436 trace1(TR_netname2user_nisplus, 1); 437 return (0); 438 case NIS_S_NOTFOUND: 439 case NIS_TRYAGAIN: 440 *err = __NSW_TRYAGAIN; 441 syslog(LOG_ERR, 442 "netname2user: (nis+ lookup): %s\n", 443 nis_sperrno(res->status)); 444 nis_freeresult(res); 445 trace1(TR_netname2user_nisplus, 1); 446 return (0); 447 default: 448 *err = __NSW_UNAVAIL; 449 syslog(LOG_ERR, "netname2user: (nis+ lookup): %s\n", 450 nis_sperrno(res->status)); 451 nis_freeresult(res); 452 trace1(TR_netname2user_nisplus, 1); 453 return (0); 454 } 455 456 if (res->objects.objects_len > 1) { 457 /* 458 * A netname belonging to more than one principal? 459 * Something wrong with cred table. should be unique. 460 * Warn user and continue. 461 */ 462 syslog(LOG_ALERT, 463 "netname2user: DES entry for %s in \ 464 directory %s not unique", 465 netname, domain); 466 } 467 468 len = ENTRY_LEN(res->objects.objects_val, 0); 469 strncpy(principal, ENTRY_VAL(res->objects.objects_val, 0), len); 470 principal[len] = '\0'; 471 nis_freeresult(res); 472 473 if (principal[0] == '\0') { 474 *err = __NSW_UNAVAIL; 475 trace1(TR_netname2user_nisplus, 1); 476 return (0); 477 } 478 479 /* 480 * 3. Use principal name to look up uid/gid information in 481 * LOCAL entry in **local** cred table. 482 */ 483 domain = nis_local_directory(); 484 if ((strlen(principal)+strlen(domain)+PKTABLE_LEN+30) > 485 (size_t)NIS_MAXNAMELEN) { 486 *err = __NSW_UNAVAIL; 487 syslog(LOG_ERR, "netname2user: principal name '%s' too long", 488 principal); 489 trace1(TR_netname2user_nisplus, 1); 490 return (0); 491 } 492 sprintf(sname, "[cname=\"%s\",auth_type=LOCAL],%s.%s", 493 principal, PKTABLE, domain); 494 if (sname[strlen(sname) - 1] != '.') 495 strcat(sname, "."); 496 497 /* must use authenticated call here */ 498 /* XXX but we cant, for now. XXX */ 499 res = nis_list(sname, USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH, 500 NULL, NULL); 501 switch (res->status) { 502 case NIS_NOTFOUND: 503 case NIS_PARTIAL: 504 case NIS_NOSUCHNAME: 505 case NIS_NOSUCHTABLE: 506 *err = __NSW_NOTFOUND; 507 nis_freeresult(res); 508 trace1(TR_netname2user_nisplus, 1); 509 return (0); 510 case NIS_S_NOTFOUND: 511 case NIS_TRYAGAIN: 512 *err = __NSW_TRYAGAIN; 513 syslog(LOG_ERR, 514 "netname2user: (nis+ lookup): %s\n", 515 nis_sperrno(res->status)); 516 nis_freeresult(res); 517 trace1(TR_netname2user_nisplus, 1); 518 return (0); 519 case NIS_SUCCESS: 520 case NIS_S_SUCCESS: 521 break; /* go and do something useful */ 522 default: 523 *err = __NSW_UNAVAIL; 524 syslog(LOG_ERR, "netname2user: (nis+ lookup): %s\n", 525 nis_sperrno(res->status)); 526 nis_freeresult(res); 527 trace1(TR_netname2user_nisplus, 1); 528 return (0); 529 } 530 531 if (res->objects.objects_len > 1) { 532 /* 533 * A principal can have more than one LOCAL entry? 534 * Something wrong with cred table. 535 * Warn user and continue. 536 */ 537 syslog(LOG_ALERT, 538 "netname2user: LOCAL entry for %s in\ 539 directory %s not unique", 540 netname, domain); 541 } 542 /* nisname LOCAL uid grp,grp,grp */ 543 *err = parse_uid_gidlist(ENTRY_VAL(res->objects.objects_val, 2), 544 /* uid */ 545 ENTRY_VAL(res->objects.objects_val, 3), /* gids */ 546 argp); 547 nis_freeresult(res); 548 trace1(TR_netname2user_nisplus, 1); 549 return (*err == __NSW_SUCCESS); 550 } 551 552 /* 553 * Build the uid and gid from the netname for users in LDAP. 554 * There is no netid container in LDAP. For this we build 555 * the netname to user data dynamically from the passwd and 556 * group data. This works only for users in a single domain. 557 * This function is an interim solution until we support a 558 * netid container in LDAP which enables us to do netname2user 559 * resolution for multiple domains. 560 */ 561 static int 562 netname2user_ldap(err, netname, argp) 563 int *err; 564 char *netname; 565 struct netid_userdata *argp; 566 { 567 568 char buf[NSS_LINELEN_PASSWD]; 569 char *p1, *p2, *lasts; 570 struct passwd pw; 571 uid_t uidnu; 572 int ngroups = 0; 573 int count; 574 char pwbuf[NSS_LINELEN_PASSWD]; 575 gid_t groups[NGROUPS_MAX]; 576 577 if (strlcpy(buf, netname, NSS_LINELEN_PASSWD) >= NSS_LINELEN_PASSWD) { 578 *err = __NSW_UNAVAIL; 579 return (0); 580 } 581 582 /* get the uid from the netname */ 583 if ((p1 = strtok_r(buf, ".", &lasts)) == NULL) { 584 *err = __NSW_UNAVAIL; 585 return (0); 586 } 587 if ((p2 = strtok_r(NULL, "@", &lasts)) == NULL) { 588 *err = __NSW_UNAVAIL; 589 return (0); 590 } 591 uidnu = atoi(p2); 592 593 /* 594 * check out the primary group and crosscheck the uid 595 * with the passwd data 596 */ 597 if ((getpwuid_r(uidnu, &pw, pwbuf, sizeof (pwbuf))) == NULL) { 598 *err = __NSW_UNAVAIL; 599 return (0); 600 } 601 602 *(argp->uidp) = pw.pw_uid; 603 *(argp->gidp) = pw.pw_gid; 604 605 /* search through all groups for membership */ 606 607 groups[0] = pw.pw_gid; 608 609 ngroups = _getgroupsbymember(pw.pw_name, groups, NGROUPS_MAX, 610 (pw.pw_gid >= 0) ? 1 : 0); 611 612 if (ngroups < 0) { 613 *err = __NSW_UNAVAIL; 614 return (0); 615 } 616 617 *(argp->gidlenp) = ngroups; 618 619 for (count = 0; count < ngroups; count++) { 620 (argp->gidlist[count]) = groups[count]; 621 } 622 623 *err = __NSW_SUCCESS; 624 return (1); 625 626 } 627 628 /* 629 * Convert network-name into unix credential 630 */ 631 int 632 netname2user(netname, uidp, gidp, gidlenp, gidlist) 633 const char netname[MAXNETNAMELEN + 1]; 634 uid_t *uidp; 635 gid_t *gidp; 636 int *gidlenp; 637 gid_t *gidlist; 638 { 639 struct __nsw_switchconfig *conf; 640 struct __nsw_lookup *look; 641 enum __nsw_parse_err perr; 642 int needfree = 1, res; 643 struct netid_userdata argp; 644 int err; 645 646 trace1(TR_netname2user, 0); 647 648 /* 649 * Take care of the special case of nobody. Compare the netname 650 * to the string "nobody". If they are equal, return the SVID 651 * standard value for nobody. 652 */ 653 654 if (strcmp(netname, "nobody") == 0) { 655 *uidp = NOBODY_UID; 656 *gidp = NOBODY_UID; 657 *gidlenp = 0; 658 return (1); 659 } 660 661 /* 662 * First we do some generic sanity checks on the name we were 663 * passed. This lets us assume they are correct in the backends. 664 * 665 * NOTE: this code only recognizes names of the form : 666 * unix.UID@domainname 667 */ 668 if (strncmp(netname, OPSYS, OPSYS_LEN) != 0) { 669 trace1(TR_netname2user, 1); 670 return (0); 671 } 672 if (! isdigit(netname[OPSYS_LEN+1])) { /* check for uid string */ 673 trace1(TR_netname2user, 1); 674 return (0); 675 } 676 677 argp.uidp = uidp; 678 argp.gidp = gidp; 679 argp.gidlenp = gidlenp; 680 argp.gidlist = gidlist; 681 mutex_lock(&serialize_netname_r); 682 683 conf = __nsw_getconfig("publickey", &perr); 684 if (! conf) { 685 conf = &publickey_default; 686 needfree = 0; 687 } else 688 needfree = 1; /* free the config structure */ 689 690 for (look = conf->lookups; look; look = look->next) { 691 if (strcmp(look->service_name, "nisplus") == 0) 692 res = netname2user_nisplus(&err, 693 (char *)netname, &argp); 694 else if (strcmp(look->service_name, "nis") == 0) 695 res = netname2user_nis(&err, (char *)netname, &argp); 696 else if (strcmp(look->service_name, "files") == 0) 697 res = netname2user_files(&err, (char *)netname, &argp); 698 else if (strcmp(look->service_name, "ldap") == 0) 699 res = netname2user_ldap(&err, (char *)netname, &argp); 700 else { 701 syslog(LOG_INFO, 702 "netname2user: unknown nameservice for publickey info '%s'\n", 703 look->service_name); 704 err = __NSW_UNAVAIL; 705 } 706 switch (look->actions[err]) { 707 case __NSW_CONTINUE : 708 break; 709 case __NSW_RETURN : 710 if (needfree) 711 __nsw_freeconfig(conf); 712 mutex_unlock(&serialize_netname_r); 713 trace1(TR_netname2user, 1); 714 return (res); 715 default : 716 syslog(LOG_ERR, 717 "netname2user: Unknown action for nameservice '%s'", 718 look->service_name); 719 } 720 } 721 if (needfree) 722 __nsw_freeconfig(conf); 723 mutex_unlock(&serialize_netname_r); 724 trace1(TR_netname2user, 1); 725 return (0); 726 } 727 728 /* 729 * Convert network-name to hostname (fully qualified) 730 * NOTE: this code only recognizes names of the form : 731 * unix.HOST@domainname 732 * 733 * This is very simple. Since the netname is of the form: 734 * unix.host@domainname 735 * We just construct the hostname using information from the domainname. 736 */ 737 int 738 netname2host(netname, hostname, hostlen) 739 const char netname[MAXNETNAMELEN + 1]; 740 char *hostname; 741 int hostlen; 742 { 743 char *p, *domainname; 744 int len, dlen; 745 746 trace1(TR_netname2host, 0); 747 748 if (!netname) { 749 syslog(LOG_ERR, "netname2host: null netname"); 750 goto bad_exit; 751 } 752 753 if (strncmp(netname, OPSYS, OPSYS_LEN) != 0) 754 goto bad_netname; 755 p = (char *)netname + OPSYS_LEN; /* skip OPSYS part */ 756 if (*p != '.') 757 goto bad_netname; 758 ++p; /* skip '.' */ 759 760 domainname = strchr(p, '@'); /* get domain name */ 761 if (domainname == 0) 762 goto bad_netname; 763 764 len = domainname - p; /* host sits between '.' and '@' */ 765 domainname++; /* skip '@' sign */ 766 767 if (len <= 0) 768 goto bad_netname; 769 770 if (hostlen < len) { 771 syslog(LOG_ERR, 772 "netname2host: insufficient space for hostname"); 773 goto bad_exit; 774 } 775 776 if (isdigit(*p)) /* don't want uid here */ 777 goto bad_netname; 778 779 if (*p == '\0') /* check for null hostname */ 780 goto bad_netname; 781 782 strncpy(hostname, p, len); 783 784 /* make into fully qualified hostname by concatenating domain part */ 785 dlen = strlen(domainname); 786 if (hostlen < (len + dlen + 2)) { 787 syslog(LOG_ERR, 788 "netname2host: insufficient space for hostname"); 789 goto bad_exit; 790 } 791 792 hostname[len] = '.'; 793 strncpy(hostname+len+1, domainname, dlen); 794 hostname[len+dlen+1] = '\0'; 795 796 trace1(TR_netname2host, 1); 797 return (1); 798 799 800 bad_netname: 801 syslog(LOG_ERR, "netname2host: invalid host netname %s", netname); 802 803 bad_exit: 804 hostname[0] = '\0'; 805 trace1(TR_netname2host, 1); 806 return (0); 807 } 808