1 /* $OpenBSD: auth.c,v 1.124 2017/09/12 06:32:07 djm Exp $ */ 2 /* 3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/stat.h> 28 #include <sys/socket.h> 29 30 #include <errno.h> 31 #include <fcntl.h> 32 #include <login_cap.h> 33 #include <paths.h> 34 #include <pwd.h> 35 #include <stdarg.h> 36 #include <stdio.h> 37 #include <string.h> 38 #include <unistd.h> 39 #include <limits.h> 40 #include <netdb.h> 41 42 #include "xmalloc.h" 43 #include "match.h" 44 #include "groupaccess.h" 45 #include "log.h" 46 #include "buffer.h" 47 #include "misc.h" 48 #include "servconf.h" 49 #include "key.h" 50 #include "hostfile.h" 51 #include "auth.h" 52 #include "auth-options.h" 53 #include "canohost.h" 54 #include "uidswap.h" 55 #include "packet.h" 56 #ifdef GSSAPI 57 #include "ssh-gss.h" 58 #endif 59 #include "authfile.h" 60 #include "monitor_wrap.h" 61 #include "authfile.h" 62 #include "ssherr.h" 63 #include "compat.h" 64 65 /* import */ 66 extern ServerOptions options; 67 extern int use_privsep; 68 69 /* Debugging messages */ 70 Buffer auth_debug; 71 int auth_debug_init; 72 73 /* 74 * Check if the user is allowed to log in via ssh. If user is listed 75 * in DenyUsers or one of user's groups is listed in DenyGroups, false 76 * will be returned. If AllowUsers isn't empty and user isn't listed 77 * there, or if AllowGroups isn't empty and one of user's groups isn't 78 * listed there, false will be returned. 79 * If the user's shell is not executable, false will be returned. 80 * Otherwise true is returned. 81 */ 82 int 83 allowed_user(struct passwd * pw) 84 { 85 struct ssh *ssh = active_state; /* XXX */ 86 struct stat st; 87 const char *hostname = NULL, *ipaddr = NULL; 88 int r; 89 u_int i; 90 91 /* Shouldn't be called if pw is NULL, but better safe than sorry... */ 92 if (!pw || !pw->pw_name) 93 return 0; 94 95 /* 96 * Deny if shell does not exist or is not executable unless we 97 * are chrooting. 98 */ 99 if (options.chroot_directory == NULL || 100 strcasecmp(options.chroot_directory, "none") == 0) { 101 char *shell = xstrdup((pw->pw_shell[0] == '\0') ? 102 _PATH_BSHELL : pw->pw_shell); /* empty = /bin/sh */ 103 104 if (stat(shell, &st) != 0) { 105 logit("User %.100s not allowed because shell %.100s " 106 "does not exist", pw->pw_name, shell); 107 free(shell); 108 return 0; 109 } 110 if (S_ISREG(st.st_mode) == 0 || 111 (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) { 112 logit("User %.100s not allowed because shell %.100s " 113 "is not executable", pw->pw_name, shell); 114 free(shell); 115 return 0; 116 } 117 free(shell); 118 } 119 120 if (options.num_deny_users > 0 || options.num_allow_users > 0 || 121 options.num_deny_groups > 0 || options.num_allow_groups > 0) { 122 hostname = auth_get_canonical_hostname(ssh, options.use_dns); 123 ipaddr = ssh_remote_ipaddr(ssh); 124 } 125 126 /* Return false if user is listed in DenyUsers */ 127 if (options.num_deny_users > 0) { 128 for (i = 0; i < options.num_deny_users; i++) { 129 r = match_user(pw->pw_name, hostname, ipaddr, 130 options.deny_users[i]); 131 if (r < 0) { 132 fatal("Invalid DenyUsers pattern \"%.100s\"", 133 options.deny_users[i]); 134 } else if (r != 0) { 135 logit("User %.100s from %.100s not allowed " 136 "because listed in DenyUsers", 137 pw->pw_name, hostname); 138 return 0; 139 } 140 } 141 } 142 /* Return false if AllowUsers isn't empty and user isn't listed there */ 143 if (options.num_allow_users > 0) { 144 for (i = 0; i < options.num_allow_users; i++) { 145 r = match_user(pw->pw_name, hostname, ipaddr, 146 options.allow_users[i]); 147 if (r < 0) { 148 fatal("Invalid AllowUsers pattern \"%.100s\"", 149 options.allow_users[i]); 150 } else if (r == 1) 151 break; 152 } 153 /* i < options.num_allow_users iff we break for loop */ 154 if (i >= options.num_allow_users) { 155 logit("User %.100s from %.100s not allowed because " 156 "not listed in AllowUsers", pw->pw_name, hostname); 157 return 0; 158 } 159 } 160 if (options.num_deny_groups > 0 || options.num_allow_groups > 0) { 161 /* Get the user's group access list (primary and supplementary) */ 162 if (ga_init(pw->pw_name, pw->pw_gid) == 0) { 163 logit("User %.100s from %.100s not allowed because " 164 "not in any group", pw->pw_name, hostname); 165 return 0; 166 } 167 168 /* Return false if one of user's groups is listed in DenyGroups */ 169 if (options.num_deny_groups > 0) 170 if (ga_match(options.deny_groups, 171 options.num_deny_groups)) { 172 ga_free(); 173 logit("User %.100s from %.100s not allowed " 174 "because a group is listed in DenyGroups", 175 pw->pw_name, hostname); 176 return 0; 177 } 178 /* 179 * Return false if AllowGroups isn't empty and one of user's groups 180 * isn't listed there 181 */ 182 if (options.num_allow_groups > 0) 183 if (!ga_match(options.allow_groups, 184 options.num_allow_groups)) { 185 ga_free(); 186 logit("User %.100s from %.100s not allowed " 187 "because none of user's groups are listed " 188 "in AllowGroups", pw->pw_name, hostname); 189 return 0; 190 } 191 ga_free(); 192 } 193 /* We found no reason not to let this user try to log on... */ 194 return 1; 195 } 196 197 /* 198 * Formats any key left in authctxt->auth_method_key for inclusion in 199 * auth_log()'s message. Also includes authxtct->auth_method_info if present. 200 */ 201 static char * 202 format_method_key(Authctxt *authctxt) 203 { 204 const struct sshkey *key = authctxt->auth_method_key; 205 const char *methinfo = authctxt->auth_method_info; 206 char *fp, *ret = NULL; 207 208 if (key == NULL) 209 return NULL; 210 211 if (key_is_cert(key)) { 212 fp = sshkey_fingerprint(key->cert->signature_key, 213 options.fingerprint_hash, SSH_FP_DEFAULT); 214 xasprintf(&ret, "%s ID %s (serial %llu) CA %s %s%s%s", 215 sshkey_type(key), key->cert->key_id, 216 (unsigned long long)key->cert->serial, 217 sshkey_type(key->cert->signature_key), 218 fp == NULL ? "(null)" : fp, 219 methinfo == NULL ? "" : ", ", 220 methinfo == NULL ? "" : methinfo); 221 free(fp); 222 } else { 223 fp = sshkey_fingerprint(key, options.fingerprint_hash, 224 SSH_FP_DEFAULT); 225 xasprintf(&ret, "%s %s%s%s", sshkey_type(key), 226 fp == NULL ? "(null)" : fp, 227 methinfo == NULL ? "" : ", ", 228 methinfo == NULL ? "" : methinfo); 229 free(fp); 230 } 231 return ret; 232 } 233 234 void 235 auth_log(Authctxt *authctxt, int authenticated, int partial, 236 const char *method, const char *submethod) 237 { 238 struct ssh *ssh = active_state; /* XXX */ 239 void (*authlog) (const char *fmt,...) = verbose; 240 const char *authmsg; 241 char *extra = NULL; 242 243 if (use_privsep && !mm_is_monitor() && !authctxt->postponed) 244 return; 245 246 /* Raise logging level */ 247 if (authenticated == 1 || 248 !authctxt->valid || 249 authctxt->failures >= options.max_authtries / 2 || 250 strcmp(method, "password") == 0) 251 authlog = logit; 252 253 if (authctxt->postponed) 254 authmsg = "Postponed"; 255 else if (partial) 256 authmsg = "Partial"; 257 else 258 authmsg = authenticated ? "Accepted" : "Failed"; 259 260 if ((extra = format_method_key(authctxt)) == NULL) { 261 if (authctxt->auth_method_info != NULL) 262 extra = xstrdup(authctxt->auth_method_info); 263 } 264 265 authlog("%s %s%s%s for %s%.100s from %.200s port %d ssh2%s%s", 266 authmsg, 267 method, 268 submethod != NULL ? "/" : "", submethod == NULL ? "" : submethod, 269 authctxt->valid ? "" : "invalid user ", 270 authctxt->user, 271 ssh_remote_ipaddr(ssh), 272 ssh_remote_port(ssh), 273 extra != NULL ? ": " : "", 274 extra != NULL ? extra : ""); 275 276 free(extra); 277 } 278 279 void 280 auth_maxtries_exceeded(Authctxt *authctxt) 281 { 282 struct ssh *ssh = active_state; /* XXX */ 283 284 error("maximum authentication attempts exceeded for " 285 "%s%.100s from %.200s port %d ssh2", 286 authctxt->valid ? "" : "invalid user ", 287 authctxt->user, 288 ssh_remote_ipaddr(ssh), 289 ssh_remote_port(ssh)); 290 packet_disconnect("Too many authentication failures"); 291 /* NOTREACHED */ 292 } 293 294 /* 295 * Check whether root logins are disallowed. 296 */ 297 int 298 auth_root_allowed(const char *method) 299 { 300 struct ssh *ssh = active_state; /* XXX */ 301 302 switch (options.permit_root_login) { 303 case PERMIT_YES: 304 return 1; 305 case PERMIT_NO_PASSWD: 306 if (strcmp(method, "publickey") == 0 || 307 strcmp(method, "hostbased") == 0 || 308 strcmp(method, "gssapi-with-mic") == 0) 309 return 1; 310 break; 311 case PERMIT_FORCED_ONLY: 312 if (forced_command) { 313 logit("Root login accepted for forced command."); 314 return 1; 315 } 316 break; 317 } 318 logit("ROOT LOGIN REFUSED FROM %.200s port %d", 319 ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); 320 return 0; 321 } 322 323 324 /* 325 * Given a template and a passwd structure, build a filename 326 * by substituting % tokenised options. Currently, %% becomes '%', 327 * %h becomes the home directory and %u the username. 328 * 329 * This returns a buffer allocated by xmalloc. 330 */ 331 char * 332 expand_authorized_keys(const char *filename, struct passwd *pw) 333 { 334 char *file, ret[PATH_MAX]; 335 int i; 336 337 file = percent_expand(filename, "h", pw->pw_dir, 338 "u", pw->pw_name, (char *)NULL); 339 340 /* 341 * Ensure that filename starts anchored. If not, be backward 342 * compatible and prepend the '%h/' 343 */ 344 if (*file == '/') 345 return (file); 346 347 i = snprintf(ret, sizeof(ret), "%s/%s", pw->pw_dir, file); 348 if (i < 0 || (size_t)i >= sizeof(ret)) 349 fatal("expand_authorized_keys: path too long"); 350 free(file); 351 return (xstrdup(ret)); 352 } 353 354 char * 355 authorized_principals_file(struct passwd *pw) 356 { 357 if (options.authorized_principals_file == NULL) 358 return NULL; 359 return expand_authorized_keys(options.authorized_principals_file, pw); 360 } 361 362 /* return ok if key exists in sysfile or userfile */ 363 HostStatus 364 check_key_in_hostfiles(struct passwd *pw, struct sshkey *key, const char *host, 365 const char *sysfile, const char *userfile) 366 { 367 char *user_hostfile; 368 struct stat st; 369 HostStatus host_status; 370 struct hostkeys *hostkeys; 371 const struct hostkey_entry *found; 372 373 hostkeys = init_hostkeys(); 374 load_hostkeys(hostkeys, host, sysfile); 375 if (userfile != NULL) { 376 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); 377 if (options.strict_modes && 378 (stat(user_hostfile, &st) == 0) && 379 ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 380 (st.st_mode & 022) != 0)) { 381 logit("Authentication refused for %.100s: " 382 "bad owner or modes for %.200s", 383 pw->pw_name, user_hostfile); 384 auth_debug_add("Ignored %.200s: bad ownership or modes", 385 user_hostfile); 386 } else { 387 temporarily_use_uid(pw); 388 load_hostkeys(hostkeys, host, user_hostfile); 389 restore_uid(); 390 } 391 free(user_hostfile); 392 } 393 host_status = check_key_in_hostkeys(hostkeys, key, &found); 394 if (host_status == HOST_REVOKED) 395 error("WARNING: revoked key for %s attempted authentication", 396 found->host); 397 else if (host_status == HOST_OK) 398 debug("%s: key for %s found at %s:%ld", __func__, 399 found->host, found->file, found->line); 400 else 401 debug("%s: key for host %s not found", __func__, host); 402 403 free_hostkeys(hostkeys); 404 405 return host_status; 406 } 407 408 static FILE * 409 auth_openfile(const char *file, struct passwd *pw, int strict_modes, 410 int log_missing, char *file_type) 411 { 412 char line[1024]; 413 struct stat st; 414 int fd; 415 FILE *f; 416 417 if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) { 418 if (log_missing || errno != ENOENT) 419 debug("Could not open %s '%s': %s", file_type, file, 420 strerror(errno)); 421 return NULL; 422 } 423 424 if (fstat(fd, &st) < 0) { 425 close(fd); 426 return NULL; 427 } 428 if (!S_ISREG(st.st_mode)) { 429 logit("User %s %s %s is not a regular file", 430 pw->pw_name, file_type, file); 431 close(fd); 432 return NULL; 433 } 434 unset_nonblock(fd); 435 if ((f = fdopen(fd, "r")) == NULL) { 436 close(fd); 437 return NULL; 438 } 439 if (strict_modes && 440 safe_path_fd(fileno(f), file, pw, line, sizeof(line)) != 0) { 441 fclose(f); 442 logit("Authentication refused: %s", line); 443 auth_debug_add("Ignored %s: %s", file_type, line); 444 return NULL; 445 } 446 447 return f; 448 } 449 450 451 FILE * 452 auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes) 453 { 454 return auth_openfile(file, pw, strict_modes, 1, "authorized keys"); 455 } 456 457 FILE * 458 auth_openprincipals(const char *file, struct passwd *pw, int strict_modes) 459 { 460 return auth_openfile(file, pw, strict_modes, 0, 461 "authorized principals"); 462 } 463 464 struct passwd * 465 getpwnamallow(const char *user) 466 { 467 struct ssh *ssh = active_state; /* XXX */ 468 extern login_cap_t *lc; 469 auth_session_t *as; 470 struct passwd *pw; 471 struct connection_info *ci = get_connection_info(1, options.use_dns); 472 473 ci->user = user; 474 parse_server_match_config(&options, ci); 475 log_change_level(options.log_level); 476 process_permitopen(ssh, &options); 477 478 pw = getpwnam(user); 479 if (pw == NULL) { 480 logit("Invalid user %.100s from %.100s port %d", 481 user, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); 482 return (NULL); 483 } 484 if (!allowed_user(pw)) 485 return (NULL); 486 if ((lc = login_getclass(pw->pw_class)) == NULL) { 487 debug("unable to get login class: %s", user); 488 return (NULL); 489 } 490 if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 || 491 auth_approval(as, lc, pw->pw_name, "ssh") <= 0) { 492 debug("Approval failure for %s", user); 493 pw = NULL; 494 } 495 if (as != NULL) 496 auth_close(as); 497 if (pw != NULL) 498 return (pwcopy(pw)); 499 return (NULL); 500 } 501 502 /* Returns 1 if key is revoked by revoked_keys_file, 0 otherwise */ 503 int 504 auth_key_is_revoked(struct sshkey *key) 505 { 506 char *fp = NULL; 507 int r; 508 509 if (options.revoked_keys_file == NULL) 510 return 0; 511 if ((fp = sshkey_fingerprint(key, options.fingerprint_hash, 512 SSH_FP_DEFAULT)) == NULL) { 513 r = SSH_ERR_ALLOC_FAIL; 514 error("%s: fingerprint key: %s", __func__, ssh_err(r)); 515 goto out; 516 } 517 518 r = sshkey_check_revoked(key, options.revoked_keys_file); 519 switch (r) { 520 case 0: 521 break; /* not revoked */ 522 case SSH_ERR_KEY_REVOKED: 523 error("Authentication key %s %s revoked by file %s", 524 sshkey_type(key), fp, options.revoked_keys_file); 525 goto out; 526 default: 527 error("Error checking authentication key %s %s in " 528 "revoked keys file %s: %s", sshkey_type(key), fp, 529 options.revoked_keys_file, ssh_err(r)); 530 goto out; 531 } 532 533 /* Success */ 534 r = 0; 535 536 out: 537 free(fp); 538 return r == 0 ? 0 : 1; 539 } 540 541 void 542 auth_debug_add(const char *fmt,...) 543 { 544 char buf[1024]; 545 va_list args; 546 547 if (!auth_debug_init) 548 return; 549 550 va_start(args, fmt); 551 vsnprintf(buf, sizeof(buf), fmt, args); 552 va_end(args); 553 buffer_put_cstring(&auth_debug, buf); 554 } 555 556 void 557 auth_debug_send(void) 558 { 559 char *msg; 560 561 if (!auth_debug_init) 562 return; 563 while (buffer_len(&auth_debug)) { 564 msg = buffer_get_string(&auth_debug, NULL); 565 packet_send_debug("%s", msg); 566 free(msg); 567 } 568 } 569 570 void 571 auth_debug_reset(void) 572 { 573 if (auth_debug_init) 574 buffer_clear(&auth_debug); 575 else { 576 buffer_init(&auth_debug); 577 auth_debug_init = 1; 578 } 579 } 580 581 struct passwd * 582 fakepw(void) 583 { 584 static struct passwd fake; 585 586 memset(&fake, 0, sizeof(fake)); 587 fake.pw_name = "NOUSER"; 588 fake.pw_passwd = 589 "$2a$06$r3.juUaHZDlIbQaO2dS9FuYxL1W9M81R1Tc92PoSNmzvpEqLkLGrK"; 590 fake.pw_gecos = "NOUSER"; 591 fake.pw_uid = (uid_t)-1; 592 fake.pw_gid = (gid_t)-1; 593 fake.pw_class = ""; 594 fake.pw_dir = "/nonexist"; 595 fake.pw_shell = "/nonexist"; 596 597 return (&fake); 598 } 599 600 /* 601 * Returns the remote DNS hostname as a string. The returned string must not 602 * be freed. NB. this will usually trigger a DNS query the first time it is 603 * called. 604 * This function does additional checks on the hostname to mitigate some 605 * attacks on legacy rhosts-style authentication. 606 * XXX is RhostsRSAAuthentication vulnerable to these? 607 * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?) 608 */ 609 610 static char * 611 remote_hostname(struct ssh *ssh) 612 { 613 struct sockaddr_storage from; 614 socklen_t fromlen; 615 struct addrinfo hints, *ai, *aitop; 616 char name[NI_MAXHOST], ntop2[NI_MAXHOST]; 617 const char *ntop = ssh_remote_ipaddr(ssh); 618 619 /* Get IP address of client. */ 620 fromlen = sizeof(from); 621 memset(&from, 0, sizeof(from)); 622 if (getpeername(ssh_packet_get_connection_in(ssh), 623 (struct sockaddr *)&from, &fromlen) < 0) { 624 debug("getpeername failed: %.100s", strerror(errno)); 625 return strdup(ntop); 626 } 627 628 debug3("Trying to reverse map address %.100s.", ntop); 629 /* Map the IP address to a host name. */ 630 if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), 631 NULL, 0, NI_NAMEREQD) != 0) { 632 /* Host name not found. Use ip address. */ 633 return strdup(ntop); 634 } 635 636 /* 637 * if reverse lookup result looks like a numeric hostname, 638 * someone is trying to trick us by PTR record like following: 639 * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 640 */ 641 memset(&hints, 0, sizeof(hints)); 642 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 643 hints.ai_flags = AI_NUMERICHOST; 644 if (getaddrinfo(name, NULL, &hints, &ai) == 0) { 645 logit("Nasty PTR record \"%s\" is set up for %s, ignoring", 646 name, ntop); 647 freeaddrinfo(ai); 648 return strdup(ntop); 649 } 650 651 /* Names are stored in lowercase. */ 652 lowercase(name); 653 654 /* 655 * Map it back to an IP address and check that the given 656 * address actually is an address of this host. This is 657 * necessary because anyone with access to a name server can 658 * define arbitrary names for an IP address. Mapping from 659 * name to IP address can be trusted better (but can still be 660 * fooled if the intruder has access to the name server of 661 * the domain). 662 */ 663 memset(&hints, 0, sizeof(hints)); 664 hints.ai_family = from.ss_family; 665 hints.ai_socktype = SOCK_STREAM; 666 if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { 667 logit("reverse mapping checking getaddrinfo for %.700s " 668 "[%s] failed.", name, ntop); 669 return strdup(ntop); 670 } 671 /* Look for the address from the list of addresses. */ 672 for (ai = aitop; ai; ai = ai->ai_next) { 673 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, 674 sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && 675 (strcmp(ntop, ntop2) == 0)) 676 break; 677 } 678 freeaddrinfo(aitop); 679 /* If we reached the end of the list, the address was not there. */ 680 if (ai == NULL) { 681 /* Address not found for the host name. */ 682 logit("Address %.100s maps to %.600s, but this does not " 683 "map back to the address.", ntop, name); 684 return strdup(ntop); 685 } 686 return strdup(name); 687 } 688 689 /* 690 * Return the canonical name of the host in the other side of the current 691 * connection. The host name is cached, so it is efficient to call this 692 * several times. 693 */ 694 695 const char * 696 auth_get_canonical_hostname(struct ssh *ssh, int use_dns) 697 { 698 static char *dnsname; 699 700 if (!use_dns) 701 return ssh_remote_ipaddr(ssh); 702 else if (dnsname != NULL) 703 return dnsname; 704 else { 705 dnsname = remote_hostname(ssh); 706 return dnsname; 707 } 708 } 709