Nicolas, Don't worry, I think when we got it to work it wasn't a very elegant solution either. We ended up modifying the standard sshd to just use a shell that we give it. If I recall right it certainly was not the bash shell. Here is a new condor_sshd. Modify your global configuration file to point to this executable. CONDOR_SSHD=/usr/bin/sshd change this to CONDOR_SSHD=/path/to/my/condor_sshd (the exe included in this file) This is a modified stock sshd daemon (openssh3.9p1) to not look at the passwd file for a shell but to just use /bin/tcsh. I have also included the modified files for the openssh. So you don't feel weird about running random binaries. Lemme know if it works. Cheers Danny Quoting Nicolas GUIOT <nicolas.guiot@xxxxxxx>: > Danny, > > I inserted a "whoami" and an "id" in sshd.sh, and it's running as me, with > correct uid/gid. > > I'am using NIS, and the CONDOR_IDS variables are correctly set to the > "condor" user, which is also NIS registered... > > After a bit of thinking, I'm quite sure the error has nothing to do with my > "-oAcceptEnv" for sshd modification, so I won't ask again about this until > this 1st problem is solved :) > > Just thought about something else : I am using /bin/bash as shell. I read > soemwhere in the doc that this script (although running on /bin/sh) was only > .csh compatible. I tried to change my shell to csh, but that didn't work > either. > > I finally added the PATH in sshd.sh : it's not a very elegant solution : If > you know better, please let me know. > > Nicolas > > > > ---------------- > On Fri, 2 Jun 2006 07:59:23 -0600 > rnayar@xxxxxxxx wrote: > > > Nicolas, > > > > This is why I asked if the user Nobody was actually running the job. There > is a > > difference between who submitted the job and what user is actually running > it. > > Are your CONDOR_IDS variable actually set to a real user? Are you using > NIS? > > Modify the scripts to echo which IDs are being used. And see whether or not > the > > jobs is running as Nobody. > > > > Danny > > > > > > Quoting Nicolas GUIOT <nicolas.guiot@xxxxxxx>: > > > > > Ok, I finally found it BUT I still nedd your help > > > > > > The thing is : it was still a problem of "command not found", for the > "grep > > > Server blabla....". > > > > > > I tryed by adding, each time, the necessary /bin and /usr/bin, etc, for > this > > > file, but once it was done, I had the exact same problem with the > > > condor_exec.exe, so I need your help : where is (or should be) the $PATH > set > > > ? because it seems it doesn't take care of it. Can it be because I > removed > > > the "-oAcceptEnv" in the sshd command ? > > > > > > Plus, to check/debug, I made it print the environment : > > > > > > _CONDOR_SCRATCH_DIR=/scratch/condor/execute/dir_19200 > > > _CONDOR_REMOTE_SPOOL_DIR=/scratch/condor/spool/cluster49.proc0.subproc0 > > > _CONDOR_ANCESTOR_19200=19205:1149254658:1012152896 > > > PATH=/ibpc/io/condor/bin:/ibpc/io/condor/bin:/ibpc/io/condor/sbin > > > _CONDOR_ANCESTOR_8054=8055:1148998380:3132411392 > > > CONDOR_CONFIG=/ibpc/io/condor/etc/condor_config > > > PWD=/scratch/condor/execute/dir_19200 > > > _CONDOR_ANCESTOR_8055=19200:1149254657:3132411480 > > > SHLVL=1 > > > _CONDOR_NPROCS=2 > > > _CONDOR_PROCNO=0 > > > _=/usr/bin/env > > > > > > > > > So, as you can see, there is no $PATH > > > > > > > > > Hope this information can help you to help me... > > > Nicolas > > > > > > > > > ---------------- > > > On Fri, 2 Jun 2006 11:24:57 +0200 > > > Nicolas GUIOT <nicolas.guiot@xxxxxxx> wrote: > > > > > > > Danny, > > > > > > > > If I type condor_q, this tells me the following : > > > > > > > > ID OWNER SUBMITTED RUN_TIME ST PRI SIZE CMD > > > > 20.0 nice-user.guio 6/2 11:20 0+00:00:58 R 0 0.0 > lamscript > > > mdrun.sh > > > > > > > > So, it seems to be ME running the job (if I correctly understood your > > > question) > > > > > > > > Do you think this can have a link with my actual problem ? which is : > > > > > > > > guiot@seurat:~/divers/MD$ tail -f > > > /ibpc/charon/condor/execute/dir_5360/sshd.out > > > > Disabling protocol version 1. Could not load host key > > > > Server listening on 0.0.0.0 port 4462. > > > > > > > > guiot@seurat:~/divers/MD$ tail > > > > -f /ibpc/charon/condor/execute/dir_5361/sshd.out Disabling protocol > > > > version 1. Could not load host key Bind to port 4466 on 0.0.0.0 > failed: > > > > Address already in use. Cannot bind any address. > > > > > > > > > > > > Nicolas > > > > > > > > > > > > ---------------- > > > > On Thu, 1 Jun 2006 11:02:06 -0600 > > > > rnayar@xxxxxxxx wrote: > > > > > > > > > Nicolas, > > > > > > > > > > Yeah you are right, it is the condor_ssh script (I wasn't at work > when I > > > wrote > > > > > that). Can you verify for me which user is actually running the job? > You > > > had > > > > > mentioned that you are using a shared file system and I'm curious, is > the > > > user > > > > > "Nobody" actually running your jobs? > > > > > > > > > > Talk to you soon, > > > > > > > > > > Danny > > > > > > > > > > > > > > > > > > > > Quoting Nicolas GUIOT <nicolas.guiot@xxxxxxx>: > > > > > > > > > > > Danny, > > > > > > > > > > > > No, not really : I can't find any $prog in sshd.sh (but found one > > > condor_ssh, > > > > > > maybe you were talking of this one...) : I personnally only added > > > "/bin/" in > > > > > > front of mkdir and sleep, so that it find them :) > > > > > > > > > > > > But this stills leads me to the sshd server that can't > start/listen > > > > > > > > > > > > Nicolas > > > > > > > > > > > > ---------------- > > > > > > On Thu, 1 Jun 2006 09:36:46 -0600 > > > > > > rnayar@xxxxxxxx wrote: > > > > > > > > > > > > > Ugg.. > > > > > > > > > > > > > > I just had written a really long answer to the problem and yet > again > > > our > > > > > > crappy > > > > > > > email service has screwed up yet again!! > > > > > > > > > > > > > > Nicolas, right what you are probably refering to is changing > "$prog" > > > to > > > > > > "./ > > > > > > > $prog" in the sshd.sh script correct? > > > > > > > > > > > > > > Danny > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Quoting Nicolas GUIOT <nicolas.guiot@xxxxxxx>: > > > > > > > > > > > > > > > Hi, > > > > > > > > > > > > > > > > Yes, I am using NFS. > > > > > > > > > > > > > > > > I'm interested in your modified sshd.sh (maybe something > could > > > help > > > > > > me...) > > > > > > > > > > > > > > > > By the way, I already touched it a bit : for example, it > couldn't > > > find > > > > > > the > > > > > > > > "mkdir" and "sleep" commands (probably the $PATH isn't set > > > anywhere, or > > > > > > it > > > > > > > > doesn't pick it where it should), but this is a minor problem, > that > > > I > > > > > > could > > > > > > > > solve... > > > > > > > > > > > > > > > > ++ > > > > > > > > Nicolas > > > > > > > > > > > > > > > > ---------------- > > > > > > > > On Wed, 31 May 2006 10:12:27 -0600 > > > > > > > > rnayar@xxxxxxxx wrote: > > > > > > > > > > > > > > > > > Nicolas, > > > > > > > > > > > > > > > > > > Hey buddy, just curious how is your grid setup? Are you using > a > > > shared > > > > > > > > > > > > > > > filesystem? Not to long ago I was running MPI jobs in the > > > parallel > > > > > > universe > > > > > > > > > > > > > > > > > without the use of a shared filesystem. I do recall seeing > some > > > of > > > > > > the > > > > > > > > things > > > > > > > > > you listed while trouble shooting the problem. Greg Thain > and > > > myself > > > > > > were > > > > > > > > able > > > > > > > > > to haxor Condor and the sshd to make it work. I can provide > the > > > > > > > > modifications > > > > > > > > > as soon I can. > > > > > > > > > > > > > > > > > > Cheers, > > > > > > > > > > > > > > > > > > Danny Nayar > > > > > > > > > New Mexico State University > > > > > > > > > > > > > > > > > > > > > > > > > > > Quoting Nicolas GUIOT <nicolas.guiot@xxxxxxx>: > > > > > > > > > > > > > > > > > > > Hi all > > > > > > > > > > > > > > > > > > > > I'm coming back on this issue. > > > > > > > > > > > > > > > > > > > > In the sshd.sh script I have by default (6.7.18, yeah I > know, I > > > plan > > > > > > to > > > > > > > > > > upgarde soon...), this line is already replaced with > > > > > > > > > > > > > > > > > > > > if grep "Server listening" sshd.out > /dev/null 2>&1 > > > > > > > > > > > > > > > > > > > > But I still have a problem, and very strange things : > > > > > > > > > > - First, I had to modify the sshd command line, since I'm > in > > > debian > > > > > > > > stable, > > > > > > > > > > and sshd is only 3.8.x, and doesn't understand > "-oAcceptEnv" , > > > so > > > > > > I > > > > > > > > removed > > > > > > > > > > it : Maybe it's the reason to my problem (if so, do you > know a > > > way > > > > > > to > > > > > > > > > > workaround this ?) > > > > > > > > > > > > > > > > > > > > - Then, when I submit the job, it says it's running > (condor_q > > > state > > > > > > is > > > > > > > > R), > > > > > > > > > > but when I check on the node, I have the following things : > > > > > > > > > > > > > > > > > > > > > guiot@seurat:~/divers/MD$ tail -f > > > > > > > > > > /ibpc/charon/condor/execute/dir_28262/sshd.out > > > > > > > > > > Disabling protocol version 1. Could not load host key > > > > > > > > > > Bind to port 4465 on 0.0.0.0 failed: Address already in > use. > > > > > > > > > > Cannot bind any address. > > > > > > > > > > > > > > > > > > > > guiot@seurat:~/divers/MD$ tail -f > > > > > > > > > > /ibpc/charon/condor/execute/dir_28264/sshd.out > > > > > > > > > > Disabling protocol version 1. Could not load host key > > > > > > > > > > Server listening on 0.0.0.0 port 4468. > > > > > > > > > > > > > > > > > > > > So, as you can see : 1 of the process seems to be fine, > and > > > the > > > > > > other > > > > > > > > not, > > > > > > > > > > but in truth, if I check a "ps ax|grep sshd", I can see > none > > > of > > > > > > them > > > > > > > > running > > > > > > > > > > (or just the one trying to be created, which changes > > > constantly) > > > > > > > > > > > > > > > > > > > > #ps ax|grep sshd > > > > > > > > > > 758 ? Ss 0:03 /usr/sbin/sshd > > > > > > > > > > 10819 ? Ss 0:00 sshd: root@pts/0 > > > > > > > > > > 28727 ? SN 0:00 /usr/sbin/sshd -p4474 > > > > > > > > > > > > > > > > > -oAuthorizedKeysFile=/scratch/condor/execute/dir_28262/tmp/0.key.pub > > > > > > > > > > -h/scratch/condor/execute/dir_28262/tmp/hostkey -De > > > -f/dev/null > > > > > > > > > > -oStrictModes=no -oPidFile=/dev/null > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > and if I check again for the process which was fine (tail > > > sshd.out), > > > > > > it > > > > > > > > keeps > > > > > > > > > > telling me it's fine, but it's listening on a new port > !!?!?! > > > > > > > > > > > > > > > > > > > > So : Is this related to the changes I had to make > > > (-oAcceptEnv), or > > > > > > is > > > > > > > > it > > > > > > > > > > something really apart ? What could I check to solve this > ? > > > > > > > > > > > > > > > > > > > > Thanks in advance > > > > > > > > > > Nicolas > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Unfortunately the jobs starts 'running' but is blocked. > For > > > some > > > > > > reason > > > > > > > > > > > > > > > > > > > it starts some connections, but does not seem to > recognize > > > them > > > > > > (and > > > > > > > > > > > then try with a next new port, again and again). I tried > to > > > look at > > > > > > the > > > > > > > > > > > > > > > > > > > files and find out what might be the reason for this. In > > > > > > > > > > > > /usr/local/condor/libexec/sshd.sh there is a line like > this > > > : > > > > > > > > > > > > > > > > > > > > > > if grep "^Server listening on 0.0.0.0 port" sshd.out > > > > /dev/null > > > > > > > > 2>&1 > > > > > > > > > > > > > > > > > > > > > > I replaced this by : > > > > > > > > > > > > > > > > > > > > > > if grep "Server listening on :: port" sshd.out > > > /dev/null > > > 2>&1 > > > > > > > > > > > > > > > > > > > > > > Not sure at all if there was a typo, but I had the '^' > this > > > on the > > > > > > two > > > > > > > > > > > > > > > > > > > computers. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > ---------------------------------------------------- > CNRS - UPR 9080 : Laboratoire de Biochimie Theorique > Institut de Biologie Physico-Chimique > 13 rue Pierre et Marie Curie > 75005 PARIS - FRANCE > > Tel : +33 158 41 51 70 > Fax : +33 158 41 50 26 > ---------------------------------------------------- > _______________________________________________ > Condor-users mailing list > Condor-users@xxxxxxxxxxx > https://lists.cs.wisc.edu/mailman/listinfo/condor-users >
/* * Copyright (c) 2000 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" RCSID("$OpenBSD: auth.c,v 1.56 2004/07/28 09:40:29 markus Exp $"); #ifdef HAVE_LOGIN_H #include <login.h> #endif #ifdef USE_SHADOW #include <shadow.h> #endif #ifdef HAVE_LIBGEN_H #include <libgen.h> #endif #include "xmalloc.h" #include "match.h" #include "groupaccess.h" #include "log.h" #include "servconf.h" #include "auth.h" #include "auth-options.h" #include "canohost.h" #include "buffer.h" #include "bufaux.h" #include "uidswap.h" #include "misc.h" #include "bufaux.h" #include "packet.h" /* import */ extern ServerOptions options; extern Buffer loginmsg; /* Debugging messages */ Buffer auth_debug; int auth_debug_init; /* * Check if the user is allowed to log in via ssh. If user is listed * in DenyUsers or one of user's groups is listed in DenyGroups, false * will be returned. If AllowUsers isn't empty and user isn't listed * there, or if AllowGroups isn't empty and one of user's groups isn't * listed there, false will be returned. * If the user's shell is not executable, false will be returned. * Otherwise true is returned. */ int allowed_user(struct passwd * pw) { struct stat st; const char *hostname = NULL, *ipaddr = NULL, *passwd = NULL; char *shell; int i; #ifdef USE_SHADOW struct spwd *spw = NULL; #endif /* Shouldn't be called if pw is NULL, but better safe than sorry... */ if (!pw || !pw->pw_name) return 0; #ifdef USE_SHADOW if (!options.use_pam) spw = getspnam(pw->pw_name); #ifdef HAS_SHADOW_EXPIRE if (!options.use_pam && spw != NULL && auth_shadow_acctexpired(spw)) return 0; #endif /* HAS_SHADOW_EXPIRE */ #endif /* USE_SHADOW */ /* grab passwd field for locked account check */ #ifdef USE_SHADOW if (spw != NULL) passwd = spw->sp_pwdp; #else passwd = pw->pw_passwd; #endif /* check for locked account */ if (!options.use_pam && passwd && *passwd) { int locked = 0; #ifdef LOCKED_PASSWD_STRING if (strcmp(passwd, LOCKED_PASSWD_STRING) == 0) locked = 1; #endif #ifdef LOCKED_PASSWD_PREFIX if (strncmp(passwd, LOCKED_PASSWD_PREFIX, strlen(LOCKED_PASSWD_PREFIX)) == 0) locked = 1; #endif #ifdef LOCKED_PASSWD_SUBSTR if (strstr(passwd, LOCKED_PASSWD_SUBSTR)) locked = 1; #endif if (locked) { logit("User %.100s not allowed because account is locked", pw->pw_name); return 0; } } /* * Get the shell from the password data. An empty shell field is * legal, and means /bin/sh. */ shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; /* Sigh. The lam boot scripts in lam 7.1.1 can't use /bin/sh * for a non-interactive shell */ #ifdef CONDOR_SSHD shell = "/bin/tcsh"; #endif /* deny if shell does not exists or is not executable */ if (stat(shell, &st) != 0) { logit("User %.100s not allowed because shell %.100s does not exist", pw->pw_name, shell); return 0; } if (S_ISREG(st.st_mode) == 0 || (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) { logit("User %.100s not allowed because shell %.100s is not executable", pw->pw_name, shell); return 0; } if (options.num_deny_users > 0 || options.num_allow_users > 0) { hostname = get_canonical_hostname(options.use_dns); ipaddr = get_remote_ipaddr(); } /* Return false if user is listed in DenyUsers */ if (options.num_deny_users > 0) { for (i = 0; i < options.num_deny_users; i++) if (match_user(pw->pw_name, hostname, ipaddr, options.deny_users[i])) { logit("User %.100s not allowed because listed in DenyUsers", pw->pw_name); return 0; } } /* Return false if AllowUsers isn't empty and user isn't listed there */ if (options.num_allow_users > 0) { for (i = 0; i < options.num_allow_users; i++) if (match_user(pw->pw_name, hostname, ipaddr, options.allow_users[i])) break; /* i < options.num_allow_users iff we break for loop */ if (i >= options.num_allow_users) { logit("User %.100s not allowed because not listed in AllowUsers", pw->pw_name); return 0; } } if (options.num_deny_groups > 0 || options.num_allow_groups > 0) { /* Get the user's group access list (primary and supplementary) */ if (ga_init(pw->pw_name, pw->pw_gid) == 0) { logit("User %.100s not allowed because not in any group", pw->pw_name); return 0; } /* Return false if one of user's groups is listed in DenyGroups */ if (options.num_deny_groups > 0) if (ga_match(options.deny_groups, options.num_deny_groups)) { ga_free(); logit("User %.100s not allowed because a group is listed in DenyGroups", pw->pw_name); return 0; } /* * Return false if AllowGroups isn't empty and one of user's groups * isn't listed there */ if (options.num_allow_groups > 0) if (!ga_match(options.allow_groups, options.num_allow_groups)) { ga_free(); logit("User %.100s not allowed because none of user's groups are listed in AllowGroups", pw->pw_name); return 0; } ga_free(); } #ifdef CUSTOM_SYS_AUTH_ALLOWED_USER if (!sys_auth_allowed_user(pw)) return 0; #endif /* We found no reason not to let this user try to log on... */ return 1; } void auth_log(Authctxt *authctxt, int authenticated, char *method, char *info) { void (*authlog) (const char *fmt,...) = verbose; char *authmsg; /* Raise logging level */ if (authenticated == 1 || !authctxt->valid || authctxt->failures >= options.max_authtries / 2 || strcmp(method, "password") == 0) authlog = logit; if (authctxt->postponed) authmsg = "Postponed"; else authmsg = authenticated ? "Accepted" : "Failed"; authlog("%s %s for %s%.100s from %.200s port %d%s", authmsg, method, authctxt->valid ? "" : "invalid user ", authctxt->user, get_remote_ipaddr(), get_remote_port(), info); #ifdef CUSTOM_FAILED_LOGIN if (authenticated == 0 && strcmp(method, "password") == 0) record_failed_login(authctxt->user, "ssh"); #endif } /* * Check whether root logins are disallowed. */ int auth_root_allowed(char *method) { switch (options.permit_root_login) { case PERMIT_YES: return 1; break; case PERMIT_NO_PASSWD: if (strcmp(method, "password") != 0) return 1; break; case PERMIT_FORCED_ONLY: if (forced_command) { logit("Root login accepted for forced command."); return 1; } break; } logit("ROOT LOGIN REFUSED FROM %.200s", get_remote_ipaddr()); return 0; } /* * Given a template and a passwd structure, build a filename * by substituting % tokenised options. Currently, %% becomes '%', * %h becomes the home directory and %u the username. * * This returns a buffer allocated by xmalloc. */ char * expand_filename(const char *filename, struct passwd *pw) { Buffer buffer; char *file; const char *cp; /* * Build the filename string in the buffer by making the appropriate * substitutions to the given file name. */ buffer_init(&buffer); for (cp = filename; *cp; cp++) { if (cp[0] == '%' && cp[1] == '%') { buffer_append(&buffer, "%", 1); cp++; continue; } if (cp[0] == '%' && cp[1] == 'h') { buffer_append(&buffer, pw->pw_dir, strlen(pw->pw_dir)); cp++; continue; } if (cp[0] == '%' && cp[1] == 'u') { buffer_append(&buffer, pw->pw_name, strlen(pw->pw_name)); cp++; continue; } buffer_append(&buffer, cp, 1); } buffer_append(&buffer, "\0", 1); /* * Ensure that filename starts anchored. If not, be backward * compatible and prepend the '%h/' */ file = xmalloc(MAXPATHLEN); cp = buffer_ptr(&buffer); if (*cp != '/') snprintf(file, MAXPATHLEN, "%s/%s", pw->pw_dir, cp); else strlcpy(file, cp, MAXPATHLEN); buffer_free(&buffer); return file; } char * authorized_keys_file(struct passwd *pw) { return expand_filename(options.authorized_keys_file, pw); } char * authorized_keys_file2(struct passwd *pw) { return expand_filename(options.authorized_keys_file2, pw); } /* return ok if key exists in sysfile or userfile */ HostStatus check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host, const char *sysfile, const char *userfile) { Key *found; char *user_hostfile; struct stat st; HostStatus host_status; /* Check if we know the host and its host key. */ found = key_new(key->type); host_status = check_host_in_hostfile(sysfile, host, key, found, NULL); if (host_status != HOST_OK && userfile != NULL) { user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); if (options.strict_modes && (stat(user_hostfile, &st) == 0) && ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || (st.st_mode & 022) != 0)) { logit("Authentication refused for %.100s: " "bad owner or modes for %.200s", pw->pw_name, user_hostfile); } else { temporarily_use_uid(pw); host_status = check_host_in_hostfile(user_hostfile, host, key, found, NULL); restore_uid(); } xfree(user_hostfile); } key_free(found); debug2("check_key_in_hostfiles: key %s for %s", host_status == HOST_OK ? "ok" : "not found", host); return host_status; } /* * Check a given file for security. This is defined as all components * of the path to the file must be owned by either the owner of * of the file or root and no directories must be group or world writable. * * XXX Should any specific check be done for sym links ? * * Takes an open file descriptor, the file name, a uid and and * error buffer plus max size as arguments. * * Returns 0 on success and -1 on failure */ int secure_filename(FILE *f, const char *file, struct passwd *pw, char *err, size_t errlen) { uid_t uid = pw->pw_uid; char buf[MAXPATHLEN], homedir[MAXPATHLEN]; char *cp; int comparehome = 0; struct stat st; if (realpath(file, buf) == NULL) { snprintf(err, errlen, "realpath %s failed: %s", file, strerror(errno)); return -1; } if (realpath(pw->pw_dir, homedir) != NULL) comparehome = 1; /* check the open file to avoid races */ if (fstat(fileno(f), &st) < 0 || (st.st_uid != 0 && st.st_uid != uid) || (st.st_mode & 022) != 0) { snprintf(err, errlen, "bad ownership or modes for file %s", buf); return -1; } /* for each component of the canonical path, walking upwards */ for (;;) { if ((cp = dirname(buf)) == NULL) { snprintf(err, errlen, "dirname() failed"); return -1; } strlcpy(buf, cp, sizeof(buf)); debug3("secure_filename: checking '%s'", buf); if (stat(buf, &st) < 0 || (st.st_uid != 0 && st.st_uid != uid) || (st.st_mode & 022) != 0) { snprintf(err, errlen, "bad ownership or modes for directory %s", buf); return -1; } /* If are passed the homedir then we can stop */ if (comparehome && strcmp(homedir, buf) == 0) { debug3("secure_filename: terminating check at '%s'", buf); break; } /* * dirname should always complete with a "/" path, * but we can be paranoid and check for "." too */ if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0)) break; } return 0; } struct passwd * getpwnamallow(const char *user) { #ifdef HAVE_LOGIN_CAP extern login_cap_t *lc; #ifdef BSD_AUTH auth_session_t *as; #endif #endif struct passwd *pw; pw = getpwnam(user); if (pw == NULL) { logit("Invalid user %.100s from %.100s", user, get_remote_ipaddr()); #ifdef CUSTOM_FAILED_LOGIN record_failed_login(user, "ssh"); #endif return (NULL); } if (!allowed_user(pw)) return (NULL); #ifdef HAVE_LOGIN_CAP if ((lc = login_getclass(pw->pw_class)) == NULL) { debug("unable to get login class: %s", user); return (NULL); } #ifdef BSD_AUTH if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 || auth_approval(as, lc, pw->pw_name, "ssh") <= 0) { debug("Approval failure for %s", user); pw = NULL; } if (as != NULL) auth_close(as); #endif #endif if (pw != NULL) return (pwcopy(pw)); return (NULL); } void auth_debug_add(const char *fmt,...) { char buf[1024]; va_list args; if (!auth_debug_init) return; va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); buffer_put_cstring(&auth_debug, buf); } void auth_debug_send(void) { char *msg; if (!auth_debug_init) return; while (buffer_len(&auth_debug)) { msg = buffer_get_string(&auth_debug, NULL); packet_send_debug("%s", msg); xfree(msg); } } void auth_debug_reset(void) { if (auth_debug_init) buffer_clear(&auth_debug); else { buffer_init(&auth_debug); auth_debug_init = 1; } } struct passwd * fakepw(void) { static struct passwd fake; memset(&fake, 0, sizeof(fake)); fake.pw_name = "NOUSER"; fake.pw_passwd = "$2a$06$r3.juUaHZDlIbQaO2dS9FuYxL1W9M81R1Tc92PoSNmzvpEqLkLGrK"; fake.pw_gecos = "NOUSER"; fake.pw_uid = (uid_t)-1; fake.pw_gid = (gid_t)-1; #ifdef HAVE_PW_CLASS_IN_PASSWD fake.pw_class = ""; #endif fake.pw_dir = "/nonexist"; fake.pw_shell = "/nonexist"; return (&fake); }
/* config.h. Generated by configure. */ /* config.h.in. Generated from configure.ac by autoheader. */ /* $Id: acconfig.h,v 1.180 2004/08/16 13:12:06 dtucker Exp $ */ /* * Copyright (c) 1999-2003 Damien Miller. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _CONFIG_H #define _CONFIG_H /* Generated automatically from acconfig.h by autoheader. */ /* Please make your changes there */ /* Define if your platform breaks doing a seteuid before a setuid */ /* #undef SETEUID_BREAKS_SETUID */ /* Define if your setreuid() is broken */ /* #undef BROKEN_SETREUID */ /* Define if your setregid() is broken */ /* #undef BROKEN_SETREGID */ /* Define if your setresuid() is broken */ /* #undef BROKEN_SETRESUID */ /* Define if your setresgid() is broken */ /* #undef BROKEN_SETRESGID */ /* Define to a Set Process Title type if your system is */ /* supported by bsd-setproctitle.c */ #define SPT_TYPE SPT_REUSEARGV /* #undef SPT_PADCHAR */ /* setgroups() NOOP allowed */ /* #undef SETGROUPS_NOOP */ /* SCO workaround */ /* #undef BROKEN_SYS_TERMIO_H */ /* Define if you have SecureWare-based protected password database */ /* #undef HAVE_SECUREWARE */ /* If your header files don't define LOGIN_PROGRAM, then use this (detected) */ /* from environment and PATH */ #define LOGIN_PROGRAM_FALLBACK "/bin/login" /* Full path of your "passwd" program */ #define _PATH_PASSWD_PROG "/usr/bin/passwd" /* Define if your password has a pw_class field */ /* #undef HAVE_PW_CLASS_IN_PASSWD */ /* Define if your password has a pw_expire field */ /* #undef HAVE_PW_EXPIRE_IN_PASSWD */ /* Define if your password has a pw_change field */ /* #undef HAVE_PW_CHANGE_IN_PASSWD */ /* Define if your system uses access rights style file descriptor passing */ /* #undef HAVE_ACCRIGHTS_IN_MSGHDR */ /* Define if your system uses ancillary data style file descriptor passing */ #define HAVE_CONTROL_IN_MSGHDR 1 /* Define if you system's inet_ntoa is busted (e.g. Irix gcc issue) */ /* #undef BROKEN_INET_NTOA */ /* Define if your system defines sys_errlist[] */ #define HAVE_SYS_ERRLIST 1 /* Define if your system defines sys_nerr */ #define HAVE_SYS_NERR 1 /* Define if your system choked on IP TOS setting */ /* #undef IP_TOS_IS_BROKEN */ /* Define if you have the getuserattr function. */ /* #undef HAVE_GETUSERATTR */ /* Define if you have the basename function. */ #define HAVE_BASENAME 1 /* Work around problematic Linux PAM modules handling of PAM_TTY */ #define PAM_TTY_KLUDGE 1 /* Define if pam_chauthtok wants real uid set to the unpriv'ed user */ /* #undef SSHPAM_CHAUTHTOK_NEEDS_RUID */ /* Use PIPES instead of a socketpair() */ /* #undef USE_PIPES */ /* Define if your snprintf is busted */ /* #undef BROKEN_SNPRINTF */ /* Define if you are on Cygwin */ /* #undef HAVE_CYGWIN */ /* Define if you have a broken realpath. */ /* #undef BROKEN_REALPATH */ /* Define if you are on NeXT */ /* #undef HAVE_NEXT */ /* Define if you are on NEWS-OS */ /* #undef HAVE_NEWS4 */ /* Define if you want to enable PAM support */ /* #undef USE_PAM */ /* Define if you want to enable AIX4's authenticate function */ /* #undef WITH_AIXAUTHENTICATE */ /* Define if your AIX loginfailed() function takes 4 arguments (AIX >= 5.2) */ /* #undef AIX_LOGINFAILED_4ARG */ /* Define if your skeychallenge() function takes 4 arguments (eg NetBSD) */ /* #undef SKEYCHALLENGE_4ARG */ /* Define if you have/want arrays (cluster-wide session managment, not C arrays) */ /* #undef WITH_IRIX_ARRAY */ /* Define if you want IRIX project management */ /* #undef WITH_IRIX_PROJECT */ /* Define if you want IRIX audit trails */ /* #undef WITH_IRIX_AUDIT */ /* Define if you want IRIX kernel jobs */ /* #undef WITH_IRIX_JOBS */ /* Location of PRNGD/EGD random number socket */ /* #undef PRNGD_SOCKET */ /* Port number of PRNGD/EGD random number socket */ /* #undef PRNGD_PORT */ /* Builtin PRNG command timeout */ #define ENTROPY_TIMEOUT_MSEC 200 /* non-privileged user for privilege separation */ #define SSH_PRIVSEP_USER "sshd" /* Define if you want to install preformatted manpages.*/ /* #undef MANTYPE */ /* Define if your ssl headers are included with #include <openssl/header.h> */ #define HAVE_OPENSSL 1 /* Define if you are linking against RSAref. Used only to print the right * message at run-time. */ /* #undef RSAREF */ /* struct timeval */ #define HAVE_STRUCT_TIMEVAL 1 /* struct utmp and struct utmpx fields */ #define HAVE_HOST_IN_UTMP 1 #define HAVE_HOST_IN_UTMPX 1 #define HAVE_ADDR_IN_UTMP 1 #define HAVE_ADDR_IN_UTMPX 1 #define HAVE_ADDR_V6_IN_UTMP 1 #define HAVE_ADDR_V6_IN_UTMPX 1 /* #undef HAVE_SYSLEN_IN_UTMPX */ #define HAVE_PID_IN_UTMP 1 #define HAVE_TYPE_IN_UTMP 1 #define HAVE_TYPE_IN_UTMPX 1 #define HAVE_TV_IN_UTMP 1 #define HAVE_TV_IN_UTMPX 1 #define HAVE_ID_IN_UTMP 1 #define HAVE_ID_IN_UTMPX 1 #define HAVE_EXIT_IN_UTMP 1 /* #undef HAVE_TIME_IN_UTMP */ /* #undef HAVE_TIME_IN_UTMPX */ /* Define if you don't want to use your system's login() call */ /* #undef DISABLE_LOGIN */ /* Define if you don't want to use pututline() etc. to write [uw]tmp */ /* #undef DISABLE_PUTUTLINE */ /* Define if you don't want to use pututxline() etc. to write [uw]tmpx */ /* #undef DISABLE_PUTUTXLINE */ /* Define if you don't want to use lastlog */ /* #undef DISABLE_LASTLOG */ /* Define if you don't want to use lastlog in session.c */ /* #undef NO_SSH_LASTLOG */ /* Define if have krb5_init_ets */ /* #undef KRB5_INIT_ETS */ /* Define if you don't want to use utmp */ /* #undef DISABLE_UTMP */ /* Define if you don't want to use utmpx */ #define DISABLE_UTMPX 1 /* Define if you don't want to use wtmp */ /* #undef DISABLE_WTMP */ /* Define if you don't want to use wtmpx */ #define DISABLE_WTMPX 1 /* Some systems need a utmpx entry for /bin/login to work */ /* #undef LOGIN_NEEDS_UTMPX */ /* Some versions of /bin/login need the TERM supplied on the commandline */ /* #undef LOGIN_NEEDS_TERM */ /* Define if your login program cannot handle end of options ("--") */ /* #undef LOGIN_NO_ENDOPT */ /* Define if you want to specify the path to your lastlog file */ /* #undef CONF_LASTLOG_FILE */ /* Define if you want to specify the path to your utmp file */ /* #undef CONF_UTMP_FILE */ /* Define if you want to specify the path to your wtmp file */ /* #undef CONF_WTMP_FILE */ /* Define if you want to specify the path to your utmpx file */ /* #undef CONF_UTMPX_FILE */ /* Define if you want to specify the path to your wtmpx file */ /* #undef CONF_WTMPX_FILE */ /* Define if you want external askpass support */ /* #undef USE_EXTERNAL_ASKPASS */ /* Define if libc defines __progname */ #define HAVE___PROGNAME 1 /* Define if compiler implements __FUNCTION__ */ #define HAVE___FUNCTION__ 1 /* Define if compiler implements __func__ */ #define HAVE___func__ 1 /* Define this is you want GSSAPI support in the version 2 protocol */ /* #undef GSSAPI */ /* Define if you want Kerberos 5 support */ /* #undef KRB5 */ /* Define this if you are using the Heimdal version of Kerberos V5 */ /* #undef HEIMDAL */ /* Define this if you want to use libkafs' AFS support */ /* #undef USE_AFS */ /* Define if you want S/Key support */ /* #undef SKEY */ /* Define if you want TCP Wrappers support */ /* #undef LIBWRAP */ /* Define if your libraries define login() */ #define HAVE_LOGIN 1 /* Define if your libraries define daemon() */ #define HAVE_DAEMON 1 /* Define if your libraries define getpagesize() */ #define HAVE_GETPAGESIZE 1 /* Define if xauth is found in your path */ #define XAUTH_PATH "/s/std/bin/xauth" /* Define if you want to allow MD5 passwords */ /* #undef HAVE_MD5_PASSWORDS */ /* Define if you want to disable shadow passwords */ /* #undef DISABLE_SHADOW */ /* Define if you want to use shadow password expire field */ #define HAS_SHADOW_EXPIRE 1 /* Define if you have Digital Unix Security Integration Architecture */ /* #undef HAVE_OSF_SIA */ /* Define if you have getpwanam(3) [SunOS 4.x] */ /* #undef HAVE_GETPWANAM */ /* Define if you have an old version of PAM which takes only one argument */ /* to pam_strerror */ /* #undef HAVE_OLD_PAM */ /* Define if you are using Solaris-derived PAM which passes pam_messages */ /* to the conversation function with an extra level of indirection */ /* #undef PAM_SUN_CODEBASE */ /* Set this to your mail directory if you don't have maillock.h */ /* #undef MAIL_DIRECTORY */ /* Data types */ #define HAVE_U_INT 1 #define HAVE_INTXX_T 1 #define HAVE_U_INTXX_T 1 #define HAVE_UINTXX_T 1 #define HAVE_INT64_T 1 #define HAVE_U_INT64_T 1 #define HAVE_U_CHAR 1 #define HAVE_SIZE_T 1 #define HAVE_SSIZE_T 1 #define HAVE_CLOCK_T 1 #define HAVE_MODE_T 1 #define HAVE_PID_T 1 #define HAVE_SA_FAMILY_T 1 #define HAVE_STRUCT_SOCKADDR_STORAGE 1 #define HAVE_STRUCT_ADDRINFO 1 #define HAVE_STRUCT_IN6_ADDR 1 #define HAVE_STRUCT_SOCKADDR_IN6 1 /* Fields in struct sockaddr_storage */ #define HAVE_SS_FAMILY_IN_SS 1 /* #undef HAVE___SS_FAMILY_IN_SS */ /* Define if you have /dev/ptmx */ /* #undef HAVE_DEV_PTMX */ /* Define if you have /dev/ptc */ /* #undef HAVE_DEV_PTS_AND_PTC */ /* Define if you need to use IP address instead of hostname in $DISPLAY */ /* #undef IPADDR_IN_DISPLAY */ /* Specify default $PATH */ #define USER_PATH "/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin" /* Specify location of ssh.pid */ #define _PATH_SSH_PIDDIR "/var/run" /* getaddrinfo is broken (if present) */ /* #undef BROKEN_GETADDRINFO */ /* updwtmpx is broken (if present) */ /* #undef BROKEN_UPDWTMPX */ /* Workaround more Linux IPv6 quirks */ #define DONT_TRY_OTHER_AF 1 /* Detect IPv4 in IPv6 mapped addresses and treat as IPv4 */ #define IPV4_IN_IPV6 1 /* Define if you have BSD auth support */ /* #undef BSD_AUTH */ /* Define if X11 doesn't support AF_UNIX sockets on that system */ /* #undef NO_X11_UNIX_SOCKETS */ /* Define if the concept of ports only accessible to superusers isn't known */ /* #undef NO_IPPORT_RESERVED_CONCEPT */ /* Needed for SCO and NeXT */ /* #undef BROKEN_SAVED_UIDS */ /* Define if your system glob() function has the GLOB_ALTDIRFUNC extension */ #define GLOB_HAS_ALTDIRFUNC 1 /* Define if your system glob() function has gl_matchc options in glob_t */ /* #undef GLOB_HAS_GL_MATCHC */ /* Define in your struct dirent expects you to allocate extra space for d_name */ /* #undef BROKEN_ONE_BYTE_DIRENT_D_NAME */ /* Define if your system has /etc/default/login */ /* #undef HAVE_ETC_DEFAULT_LOGIN */ /* Define if your getopt(3) defines and uses optreset */ /* #undef HAVE_GETOPT_OPTRESET */ /* Define on *nto-qnx systems */ /* #undef MISSING_NFDBITS */ /* Define on *nto-qnx systems */ /* #undef MISSING_HOWMANY */ /* Define on *nto-qnx systems */ /* #undef MISSING_FD_MASK */ /* Define if you want smartcard support */ /* #undef SMARTCARD */ /* Define if you want smartcard support using sectok */ /* #undef USE_SECTOK */ /* Define if you want smartcard support using OpenSC */ /* #undef USE_OPENSC */ /* Define if you want to use OpenSSL's internally seeded PRNG only */ #define OPENSSL_PRNG_ONLY 1 /* Define if you shouldn't strip 'tty' from your ttyname in [uw]tmp */ /* #undef WITH_ABBREV_NO_TTY */ /* Define if you want a different $PATH for the superuser */ /* #undef SUPERUSER_PATH */ /* Path that unprivileged child will chroot() to in privep mode */ /* #undef PRIVSEP_PATH */ /* Define if your platform needs to skip post auth file descriptor passing */ /* #undef DISABLE_FD_PASSING */ /* Silly mkstemp() */ #define HAVE_STRICT_MKSTEMP 1 /* Some systems put this outside of libc */ #define HAVE_NANOSLEEP 1 /* Define if sshd somehow reacquires a controlling TTY after setsid() */ /* #undef SSHD_ACQUIRES_CTTY */ /* Define if cmsg_type is not passed correctly */ /* #undef BROKEN_CMSG_TYPE */ /* * Define to whatever link() returns for "not supported" if it doesn't * return EOPNOTSUPP. */ #define LINK_OPNOTSUPP_ERRNO EPERM /* Strings used in /etc/passwd to denote locked account */ /* #undef LOCKED_PASSWD_STRING */ #define LOCKED_PASSWD_PREFIX "!" /* #undef LOCKED_PASSWD_SUBSTR */ /* Define if getrrsetbyname() exists */ /* #undef HAVE_GETRRSETBYNAME */ /* Define if HEADER.ad exists in arpa/nameser.h */ #define HAVE_HEADER_AD 1 /* Define if your resolver libs need this for getrrsetbyname */ /* #undef BIND_8_COMPAT */ /* Define if you have /proc/$pid/fd */ #define HAVE_PROC_PID 1 /* Define to 1 if the `getpgrp' function requires zero arguments. */ #define GETPGRP_VOID 1 /* Define to 1 if you have the `arc4random' function. */ /* #undef HAVE_ARC4RANDOM */ /* Define to 1 if you have the `b64_ntop' function. */ /* #undef HAVE_B64_NTOP */ /* Define to 1 if you have the `b64_pton' function. */ /* #undef HAVE_B64_PTON */ /* Define to 1 if you have the `bcopy' function. */ #define HAVE_BCOPY 1 /* Define to 1 if you have the `bindresvport_sa' function. */ /* #undef HAVE_BINDRESVPORT_SA */ /* Define to 1 if you have the <bstring.h> header file. */ /* #undef HAVE_BSTRING_H */ /* Define to 1 if you have the `clock' function. */ #define HAVE_CLOCK 1 /* Define to 1 if you have the `closefrom' function. */ /* #undef HAVE_CLOSEFROM */ /* Define if gai_strerror() returns const char * */ #define HAVE_CONST_GAI_STRERROR_PROTO 1 /* Define to 1 if you have the <crypt.h> header file. */ #define HAVE_CRYPT_H 1 /* Define to 1 if you have the declaration of `h_errno', and to 0 if you don't. */ #define HAVE_DECL_H_ERRNO 1 /* Define to 1 if you have the <dirent.h> header file. */ #define HAVE_DIRENT_H 1 /* Define to 1 if you have the `dirfd' function. */ #define HAVE_DIRFD 1 /* Define to 1 if you have the `dirname' function. */ #define HAVE_DIRNAME 1 /* Define to 1 if you have the <endian.h> header file. */ #define HAVE_ENDIAN_H 1 /* Define to 1 if you have the `endutent' function. */ #define HAVE_ENDUTENT 1 /* Define to 1 if you have the `endutxent' function. */ #define HAVE_ENDUTXENT 1 /* Define to 1 if you have the `fchmod' function. */ #define HAVE_FCHMOD 1 /* Define to 1 if you have the `fchown' function. */ #define HAVE_FCHOWN 1 /* Define to 1 if you have the <features.h> header file. */ #define HAVE_FEATURES_H 1 /* Define to 1 if you have the <floatingpoint.h> header file. */ /* #undef HAVE_FLOATINGPOINT_H */ /* Define to 1 if you have the `freeaddrinfo' function. */ #define HAVE_FREEADDRINFO 1 /* Define to 1 if you have the `futimes' function. */ #define HAVE_FUTIMES 1 /* Define to 1 if you have the `gai_strerror' function. */ #define HAVE_GAI_STRERROR 1 /* Define to 1 if you have the `getaddrinfo' function. */ #define HAVE_GETADDRINFO 1 /* Define to 1 if you have the `getcwd' function. */ #define HAVE_GETCWD 1 /* Define to 1 if you have the `getgrouplist' function. */ #define HAVE_GETGROUPLIST 1 /* Define to 1 if you have the `getluid' function. */ /* #undef HAVE_GETLUID */ /* Define to 1 if you have the `getnameinfo' function. */ #define HAVE_GETNAMEINFO 1 /* Define to 1 if you have the `getopt' function. */ #define HAVE_GETOPT 1 /* Define to 1 if you have the <getopt.h> header file. */ #define HAVE_GETOPT_H 1 /* Define to 1 if you have the `getpeereid' function. */ /* #undef HAVE_GETPEEREID */ /* Define to 1 if you have the `getpwanam' function. */ /* #undef HAVE_GETPWANAM */ /* Define to 1 if you have the `getrlimit' function. */ #define HAVE_GETRLIMIT 1 /* Define to 1 if you have the `getrusage' function. */ /* #undef HAVE_GETRUSAGE */ /* Define to 1 if you have the `gettimeofday' function. */ #define HAVE_GETTIMEOFDAY 1 /* Define to 1 if you have the `getttyent' function. */ #define HAVE_GETTTYENT 1 /* Define to 1 if you have the `getutent' function. */ #define HAVE_GETUTENT 1 /* Define to 1 if you have the `getutid' function. */ #define HAVE_GETUTID 1 /* Define to 1 if you have the `getutline' function. */ #define HAVE_GETUTLINE 1 /* Define to 1 if you have the `getutxent' function. */ #define HAVE_GETUTXENT 1 /* Define to 1 if you have the `getutxid' function. */ #define HAVE_GETUTXID 1 /* Define to 1 if you have the `getutxline' function. */ #define HAVE_GETUTXLINE 1 /* Define to 1 if you have the `glob' function. */ #define HAVE_GLOB 1 /* Define to 1 if you have the <glob.h> header file. */ #define HAVE_GLOB_H 1 /* Define to 1 if you have the <gssapi_generic.h> header file. */ /* #undef HAVE_GSSAPI_GENERIC_H */ /* Define to 1 if you have the <gssapi/gssapi_generic.h> header file. */ /* #undef HAVE_GSSAPI_GSSAPI_GENERIC_H */ /* Define to 1 if you have the <gssapi/gssapi.h> header file. */ /* #undef HAVE_GSSAPI_GSSAPI_H */ /* Define to 1 if you have the <gssapi/gssapi_krb5.h> header file. */ /* #undef HAVE_GSSAPI_GSSAPI_KRB5_H */ /* Define to 1 if you have the <gssapi.h> header file. */ /* #undef HAVE_GSSAPI_H */ /* Define to 1 if you have the <gssapi_krb5.h> header file. */ /* #undef HAVE_GSSAPI_KRB5_H */ /* Define to 1 if you have the <ia.h> header file. */ /* #undef HAVE_IA_H */ /* Define to 1 if you have the `inet_aton' function. */ #define HAVE_INET_ATON 1 /* Define to 1 if you have the `inet_ntoa' function. */ #define HAVE_INET_NTOA 1 /* Define to 1 if you have the `inet_ntop' function. */ #define HAVE_INET_NTOP 1 /* Define to 1 if you have the `innetgr' function. */ #define HAVE_INNETGR 1 /* Define to 1 if you have the <inttypes.h> header file. */ #define HAVE_INTTYPES_H 1 /* Define to 1 if you have the <lastlog.h> header file. */ #define HAVE_LASTLOG_H 1 /* Define to 1 if you have the `crypt' library (-lcrypt). */ /* #undef HAVE_LIBCRYPT */ /* Define to 1 if you have the `dl' library (-ldl). */ /* #undef HAVE_LIBDL */ /* Define to 1 if you have the <libgen.h> header file. */ #define HAVE_LIBGEN_H 1 /* Define to 1 if you have the `nsl' library (-lnsl). */ #define HAVE_LIBNSL 1 /* Define to 1 if you have the `pam' library (-lpam). */ /* #undef HAVE_LIBPAM */ /* Define to 1 if you have the `sectok' library (-lsectok). */ /* #undef HAVE_LIBSECTOK */ /* Define to 1 if you have the `socket' library (-lsocket). */ /* #undef HAVE_LIBSOCKET */ /* Define to 1 if you have the <libutil.h> header file. */ /* #undef HAVE_LIBUTIL_H */ /* Define to 1 if you have the `xnet' library (-lxnet). */ /* #undef HAVE_LIBXNET */ /* Define to 1 if you have the `z' library (-lz). */ #define HAVE_LIBZ 1 /* Define to 1 if you have the <limits.h> header file. */ #define HAVE_LIMITS_H 1 /* Define to 1 if you have the <login_cap.h> header file. */ /* #undef HAVE_LOGIN_CAP_H */ /* Define to 1 if you have the `login_getcapbool' function. */ /* #undef HAVE_LOGIN_GETCAPBOOL */ /* Define to 1 if you have the <login.h> header file. */ /* #undef HAVE_LOGIN_H */ /* Define to 1 if you have the `logout' function. */ #define HAVE_LOGOUT 1 /* Define to 1 if you have the `logwtmp' function. */ #define HAVE_LOGWTMP 1 /* Define to 1 if you have the <maillock.h> header file. */ /* #undef HAVE_MAILLOCK_H */ /* Define to 1 if you have the `md5_crypt' function. */ /* #undef HAVE_MD5_CRYPT */ /* Define to 1 if you have the `memmove' function. */ #define HAVE_MEMMOVE 1 /* Define to 1 if you have the <memory.h> header file. */ #define HAVE_MEMORY_H 1 /* Define to 1 if you have the `mkdtemp' function. */ #define HAVE_MKDTEMP 1 /* Define to 1 if you have the `mmap' function. */ #define HAVE_MMAP 1 /* Define to 1 if you have the <ndir.h> header file. */ /* #undef HAVE_NDIR_H */ /* Define to 1 if you have the <netdb.h> header file. */ #define HAVE_NETDB_H 1 /* Define to 1 if you have the <netgroup.h> header file. */ /* #undef HAVE_NETGROUP_H */ /* Define to 1 if you have the <netinet/in_systm.h> header file. */ #define HAVE_NETINET_IN_SYSTM_H 1 /* Define to 1 if you have the `ngetaddrinfo' function. */ /* #undef HAVE_NGETADDRINFO */ /* Define to 1 if you have the `nsleep' function. */ /* #undef HAVE_NSLEEP */ /* Define to 1 if you have the `ogetaddrinfo' function. */ /* #undef HAVE_OGETADDRINFO */ /* Define to 1 if you have the `openlog_r' function. */ /* #undef HAVE_OPENLOG_R */ /* Define to 1 if you have the `openpty' function. */ #define HAVE_OPENPTY 1 /* Define to 1 if you have the `pam_getenvlist' function. */ /* #undef HAVE_PAM_GETENVLIST */ /* Define to 1 if you have the <pam/pam_appl.h> header file. */ /* #undef HAVE_PAM_PAM_APPL_H */ /* Define to 1 if you have the `pam_putenv' function. */ /* #undef HAVE_PAM_PUTENV */ /* Define to 1 if you have the <paths.h> header file. */ #define HAVE_PATHS_H 1 /* Define to 1 if you have the `prctl' function. */ #define HAVE_PRCTL 1 /* Define to 1 if you have the `pstat' function. */ /* #undef HAVE_PSTAT */ /* Define to 1 if you have the <pty.h> header file. */ #define HAVE_PTY_H 1 /* Define to 1 if you have the `pututline' function. */ #define HAVE_PUTUTLINE 1 /* Define to 1 if you have the `pututxline' function. */ #define HAVE_PUTUTXLINE 1 /* Define to 1 if you have the `readpassphrase' function. */ /* #undef HAVE_READPASSPHRASE */ /* Define to 1 if you have the <readpassphrase.h> header file. */ /* #undef HAVE_READPASSPHRASE_H */ /* Define to 1 if you have the `realpath' function. */ #define HAVE_REALPATH 1 /* Define to 1 if you have the `recvmsg' function. */ #define HAVE_RECVMSG 1 /* Define to 1 if you have the <rpc/types.h> header file. */ #define HAVE_RPC_TYPES_H 1 /* Define to 1 if you have the `rresvport_af' function. */ #define HAVE_RRESVPORT_AF 1 /* Define to 1 if you have the <sectok.h> header file. */ /* #undef HAVE_SECTOK_H */ /* Define to 1 if you have the <security/pam_appl.h> header file. */ #define HAVE_SECURITY_PAM_APPL_H 1 /* Define to 1 if you have the `sendmsg' function. */ #define HAVE_SENDMSG 1 /* Define to 1 if you have the `setauthdb' function. */ /* #undef HAVE_SETAUTHDB */ /* Define to 1 if you have the `setdtablesize' function. */ /* #undef HAVE_SETDTABLESIZE */ /* Define to 1 if you have the `setegid' function. */ #define HAVE_SETEGID 1 /* Define to 1 if you have the `setenv' function. */ #define HAVE_SETENV 1 /* Define to 1 if you have the `seteuid' function. */ #define HAVE_SETEUID 1 /* Define to 1 if you have the `setgroups' function. */ #define HAVE_SETGROUPS 1 /* Define to 1 if you have the `setlogin' function. */ /* #undef HAVE_SETLOGIN */ /* Define to 1 if you have the `setluid' function. */ /* #undef HAVE_SETLUID */ /* Define to 1 if you have the `setpcred' function. */ /* #undef HAVE_SETPCRED */ /* Define to 1 if you have the `setproctitle' function. */ /* #undef HAVE_SETPROCTITLE */ /* Define to 1 if you have the `setregid' function. */ #define HAVE_SETREGID 1 /* Define to 1 if you have the `setresgid' function. */ #define HAVE_SETRESGID 1 /* Define to 1 if you have the `setresuid' function. */ #define HAVE_SETRESUID 1 /* Define to 1 if you have the `setreuid' function. */ #define HAVE_SETREUID 1 /* Define to 1 if you have the `setrlimit' function. */ #define HAVE_SETRLIMIT 1 /* Define to 1 if you have the `setsid' function. */ #define HAVE_SETSID 1 /* Define to 1 if you have the `setutent' function. */ #define HAVE_SETUTENT 1 /* Define to 1 if you have the `setutxent' function. */ #define HAVE_SETUTXENT 1 /* Define to 1 if you have the `setvbuf' function. */ #define HAVE_SETVBUF 1 /* Define to 1 if you have the <shadow.h> header file. */ #define HAVE_SHADOW_H 1 /* Define to 1 if you have the `sigaction' function. */ #define HAVE_SIGACTION 1 /* Define to 1 if you have the `sigvec' function. */ #define HAVE_SIGVEC 1 /* Define to 1 if the system has the type `sig_atomic_t'. */ #define HAVE_SIG_ATOMIC_T 1 /* Define to 1 if you have the `snprintf' function. */ #define HAVE_SNPRINTF 1 /* Define to 1 if you have the `socketpair' function. */ #define HAVE_SOCKETPAIR 1 /* Define to 1 if you have the <stddef.h> header file. */ #define HAVE_STDDEF_H 1 /* Define to 1 if you have the <stdint.h> header file. */ #define HAVE_STDINT_H 1 /* Define to 1 if you have the <stdlib.h> header file. */ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the `strerror' function. */ #define HAVE_STRERROR 1 /* Define to 1 if you have the `strftime' function. */ #define HAVE_STRFTIME 1 /* Define to 1 if you have the <strings.h> header file. */ #define HAVE_STRINGS_H 1 /* Define to 1 if you have the <string.h> header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have the `strlcat' function. */ /* #undef HAVE_STRLCAT */ /* Define to 1 if you have the `strlcpy' function. */ /* #undef HAVE_STRLCPY */ /* Define to 1 if you have the `strmode' function. */ /* #undef HAVE_STRMODE */ /* Define to 1 if you have the `strnvis' function. */ /* #undef HAVE_STRNVIS */ /* Define to 1 if you have the `strsep' function. */ #define HAVE_STRSEP 1 /* Define to 1 if you have the `strtoul' function. */ #define HAVE_STRTOUL 1 /* Define to 1 if `st_blksize' is member of `struct stat'. */ #define HAVE_STRUCT_STAT_ST_BLKSIZE 1 /* Define to 1 if the system has the type `struct timespec'. */ #define HAVE_STRUCT_TIMESPEC 1 /* Define to 1 if you have the `sysconf' function. */ #define HAVE_SYSCONF 1 /* Define to 1 if you have the <sys/audit.h> header file. */ /* #undef HAVE_SYS_AUDIT_H */ /* Define to 1 if you have the <sys/bitypes.h> header file. */ #define HAVE_SYS_BITYPES_H 1 /* Define to 1 if you have the <sys/bsdtty.h> header file. */ /* #undef HAVE_SYS_BSDTTY_H */ /* Define to 1 if you have the <sys/cdefs.h> header file. */ #define HAVE_SYS_CDEFS_H 1 /* Define to 1 if you have the <sys/dir.h> header file. */ #define HAVE_SYS_DIR_H 1 /* Define to 1 if you have the <sys/mman.h> header file. */ #define HAVE_SYS_MMAN_H 1 /* Define to 1 if you have the <sys/ndir.h> header file. */ /* #undef HAVE_SYS_NDIR_H */ /* Define to 1 if you have the <sys/prctl.h> header file. */ #define HAVE_SYS_PRCTL_H 1 /* Define to 1 if you have the <sys/pstat.h> header file. */ /* #undef HAVE_SYS_PSTAT_H */ /* Define to 1 if you have the <sys/ptms.h> header file. */ /* #undef HAVE_SYS_PTMS_H */ /* Define to 1 if you have the <sys/select.h> header file. */ #define HAVE_SYS_SELECT_H 1 /* Define to 1 if you have the <sys/stat.h> header file. */ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the <sys/stream.h> header file. */ /* #undef HAVE_SYS_STREAM_H */ /* Define to 1 if you have the <sys/stropts.h> header file. */ #define HAVE_SYS_STROPTS_H 1 /* Define to 1 if you have the <sys/strtio.h> header file. */ /* #undef HAVE_SYS_STRTIO_H */ /* Define to 1 if you have the <sys/sysmacros.h> header file. */ #define HAVE_SYS_SYSMACROS_H 1 /* Define to 1 if you have the <sys/timers.h> header file. */ /* #undef HAVE_SYS_TIMERS_H */ /* Define to 1 if you have the <sys/time.h> header file. */ #define HAVE_SYS_TIME_H 1 /* Define to 1 if you have the <sys/types.h> header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the <sys/un.h> header file. */ #define HAVE_SYS_UN_H 1 /* Define to 1 if you have the `tcgetpgrp' function. */ #define HAVE_TCGETPGRP 1 /* Define to 1 if you have the `tcsendbreak' function. */ #define HAVE_TCSENDBREAK 1 /* Define to 1 if you have the `time' function. */ #define HAVE_TIME 1 /* Define to 1 if you have the <time.h> header file. */ #define HAVE_TIME_H 1 /* Define to 1 if you have the <tmpdir.h> header file. */ /* #undef HAVE_TMPDIR_H */ /* Define to 1 if you have the `truncate' function. */ #define HAVE_TRUNCATE 1 /* Define to 1 if you have the <ttyent.h> header file. */ #define HAVE_TTYENT_H 1 /* Define to 1 if you have the <unistd.h> header file. */ #define HAVE_UNISTD_H 1 /* Define to 1 if you have the `unsetenv' function. */ #define HAVE_UNSETENV 1 /* Define to 1 if you have the `updwtmp' function. */ #define HAVE_UPDWTMP 1 /* Define to 1 if you have the `updwtmpx' function. */ #define HAVE_UPDWTMPX 1 /* Define to 1 if you have the <usersec.h> header file. */ /* #undef HAVE_USERSEC_H */ /* Define to 1 if you have the <util.h> header file. */ /* #undef HAVE_UTIL_H */ /* Define to 1 if you have the `utimes' function. */ #define HAVE_UTIMES 1 /* Define to 1 if you have the <utime.h> header file. */ #define HAVE_UTIME_H 1 /* Define to 1 if you have the `utmpname' function. */ #define HAVE_UTMPNAME 1 /* Define to 1 if you have the `utmpxname' function. */ #define HAVE_UTMPXNAME 1 /* Define to 1 if you have the <utmpx.h> header file. */ #define HAVE_UTMPX_H 1 /* Define to 1 if you have the <utmp.h> header file. */ #define HAVE_UTMP_H 1 /* Define to 1 if you have the `vhangup' function. */ #define HAVE_VHANGUP 1 /* Define to 1 if you have the <vis.h> header file. */ /* #undef HAVE_VIS_H */ /* Define to 1 if you have the `vsnprintf' function. */ #define HAVE_VSNPRINTF 1 /* Define to 1 if you have the `waitpid' function. */ #define HAVE_WAITPID 1 /* Define to 1 if you have the `_getlong' function. */ #define HAVE__GETLONG 1 /* Define to 1 if you have the `_getpty' function. */ /* #undef HAVE__GETPTY */ /* Define to 1 if you have the `_getshort' function. */ #define HAVE__GETSHORT 1 /* Define to 1 if you have the `__b64_ntop' function. */ /* #undef HAVE___B64_NTOP */ /* Define to 1 if you have the `__b64_pton' function. */ /* #undef HAVE___B64_PTON */ /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "" /* Define to the full name of this package. */ #define PACKAGE_NAME "" /* Define to the full name and version of this package. */ #define PACKAGE_STRING "" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "" /* Define to the version of this package. */ #define PACKAGE_VERSION "" /* The size of a `char', as computed by sizeof. */ #define SIZEOF_CHAR 1 /* The size of a `int', as computed by sizeof. */ #define SIZEOF_INT 4 /* The size of a `long int', as computed by sizeof. */ #define SIZEOF_LONG_INT 4 /* The size of a `long long int', as computed by sizeof. */ #define SIZEOF_LONG_LONG_INT 8 /* The size of a `short int', as computed by sizeof. */ #define SIZEOF_SHORT_INT 2 /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel and VAX). */ /* #undef WORDS_BIGENDIAN */ /* Number of bits in a file offset, on hosts where this is settable. */ #define _FILE_OFFSET_BITS 64 /* Define for large files, on AIX-style hosts. */ /* #undef _LARGE_FILES */ /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus /* #undef inline */ #endif /* type to use in place of socklen_t if not defined */ /* #undef socklen_t */ #define CONDOR_SSHD /* ******************* Shouldn't need to edit below this line ************** */ #endif /* _CONFIG_H */
/* * Copyright (c) 1995 Tatu Ylonen <ylo@xxxxxxxxx>, Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 support by Markus Friedl. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" RCSID("$OpenBSD: session.c,v 1.180 2004/07/28 09:40:29 markus Exp $"); #include "ssh.h" #include "ssh1.h" #include "ssh2.h" #include "xmalloc.h" #include "sshpty.h" #include "packet.h" #include "buffer.h" #include "match.h" #include "uidswap.h" #include "compat.h" #include "channels.h" #include "bufaux.h" #include "auth.h" #include "auth-options.h" #include "pathnames.h" #include "log.h" #include "servconf.h" #include "sshlogin.h" #include "serverloop.h" #include "canohost.h" #include "session.h" #include "monitor_wrap.h" #if defined(KRB5) && defined(USE_AFS) #include <kafs.h> #endif #ifdef GSSAPI #include "ssh-gss.h" #endif /* func */ Session *session_new(void); void session_set_fds(Session *, int, int, int); void session_pty_cleanup(Session *); void session_proctitle(Session *); int session_setup_x11fwd(Session *); void do_exec_pty(Session *, const char *); void do_exec_no_pty(Session *, const char *); void do_exec(Session *, const char *); void do_login(Session *, const char *); #ifdef LOGIN_NEEDS_UTMPX static void do_pre_login(Session *s); #endif void do_child(Session *, const char *); void do_motd(void); int check_quietlogin(Session *, const char *); static void do_authenticated1(Authctxt *); static void do_authenticated2(Authctxt *); static int session_pty_req(Session *); /* import */ extern ServerOptions options; extern char *__progname; extern int log_stderr; extern int debug_flag; extern u_int utmp_len; extern int startup_pipe; extern void destroy_sensitive_data(void); extern Buffer loginmsg; /* original command from peer. */ const char *original_command = NULL; /* data */ #define MAX_SESSIONS 10 Session sessions[MAX_SESSIONS]; #ifdef HAVE_LOGIN_CAP login_cap_t *lc; #endif static int is_child = 0; /* Name and directory of socket for authentication agent forwarding. */ static char *auth_sock_name = NULL; static char *auth_sock_dir = NULL; /* removes the agent forwarding socket */ static void auth_sock_cleanup_proc(struct passwd *pw) { if (auth_sock_name != NULL) { temporarily_use_uid(pw); unlink(auth_sock_name); rmdir(auth_sock_dir); auth_sock_name = NULL; restore_uid(); } } static int auth_input_request_forwarding(struct passwd * pw) { Channel *nc; int sock; struct sockaddr_un sunaddr; if (auth_sock_name != NULL) { error("authentication forwarding requested twice."); return 0; } /* Temporarily drop privileged uid for mkdir/bind. */ temporarily_use_uid(pw); /* Allocate a buffer for the socket name, and format the name. */ auth_sock_name = xmalloc(MAXPATHLEN); auth_sock_dir = xmalloc(MAXPATHLEN); strlcpy(auth_sock_dir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN); /* Create private directory for socket */ if (mkdtemp(auth_sock_dir) == NULL) { packet_send_debug("Agent forwarding disabled: " "mkdtemp() failed: %.100s", strerror(errno)); restore_uid(); xfree(auth_sock_name); xfree(auth_sock_dir); auth_sock_name = NULL; auth_sock_dir = NULL; return 0; } snprintf(auth_sock_name, MAXPATHLEN, "%s/agent.%ld", auth_sock_dir, (long) getpid()); /* Create the socket. */ sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) packet_disconnect("socket: %.100s", strerror(errno)); /* Bind it to the name. */ memset(&sunaddr, 0, sizeof(sunaddr)); sunaddr.sun_family = AF_UNIX; strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path)); if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) packet_disconnect("bind: %.100s", strerror(errno)); /* Restore the privileged uid. */ restore_uid(); /* Start listening on the socket. */ if (listen(sock, SSH_LISTEN_BACKLOG) < 0) packet_disconnect("listen: %.100s", strerror(errno)); /* Allocate a channel for the authentication agent socket. */ nc = channel_new("auth socket", SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "auth socket", 1); strlcpy(nc->path, auth_sock_name, sizeof(nc->path)); return 1; } static void display_loginmsg(void) { if (buffer_len(&loginmsg) > 0) { buffer_append(&loginmsg, "\0", 1); printf("%s", (char *)buffer_ptr(&loginmsg)); buffer_clear(&loginmsg); } } void do_authenticated(Authctxt *authctxt) { setproctitle("%s", authctxt->pw->pw_name); /* * Cancel the alarm we set to limit the time taken for * authentication. */ alarm(0); if (startup_pipe != -1) { close(startup_pipe); startup_pipe = -1; } /* setup the channel layer */ if (!no_port_forwarding_flag && options.allow_tcp_forwarding) channel_permit_all_opens(); if (compat20) do_authenticated2(authctxt); else do_authenticated1(authctxt); do_cleanup(authctxt); } /* * Prepares for an interactive session. This is called after the user has * been successfully authenticated. During this message exchange, pseudo * terminals are allocated, X11, TCP/IP, and authentication agent forwardings * are requested, etc. */ static void do_authenticated1(Authctxt *authctxt) { Session *s; char *command; int success, type, screen_flag; int enable_compression_after_reply = 0; u_int proto_len, data_len, dlen, compression_level = 0; s = session_new(); s->authctxt = authctxt; s->pw = authctxt->pw; /* * We stay in this loop until the client requests to execute a shell * or a command. */ for (;;) { success = 0; /* Get a packet from the client. */ type = packet_read(); /* Process the packet. */ switch (type) { case SSH_CMSG_REQUEST_COMPRESSION: compression_level = packet_get_int(); packet_check_eom(); if (compression_level < 1 || compression_level > 9) { packet_send_debug("Received invalid compression level %d.", compression_level); break; } if (!options.compression) { debug2("compression disabled"); break; } /* Enable compression after we have responded with SUCCESS. */ enable_compression_after_reply = 1; success = 1; break; case SSH_CMSG_REQUEST_PTY: success = session_pty_req(s); break; case SSH_CMSG_X11_REQUEST_FORWARDING: s->auth_proto = packet_get_string(&proto_len); s->auth_data = packet_get_string(&data_len); screen_flag = packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER; debug2("SSH_PROTOFLAG_SCREEN_NUMBER: %d", screen_flag); if (packet_remaining() == 4) { if (!screen_flag) debug2("Buggy client: " "X11 screen flag missing"); s->screen = packet_get_int(); } else { s->screen = 0; } packet_check_eom(); success = session_setup_x11fwd(s); if (!success) { xfree(s->auth_proto); xfree(s->auth_data); s->auth_proto = NULL; s->auth_data = NULL; } break; case SSH_CMSG_AGENT_REQUEST_FORWARDING: if (no_agent_forwarding_flag || compat13) { debug("Authentication agent forwarding not permitted for this authentication."); break; } debug("Received authentication agent forwarding request."); success = auth_input_request_forwarding(s->pw); break; case SSH_CMSG_PORT_FORWARD_REQUEST: if (no_port_forwarding_flag) { debug("Port forwarding not permitted for this authentication."); break; } if (!options.allow_tcp_forwarding) { debug("Port forwarding not permitted."); break; } debug("Received TCP/IP port forwarding request."); channel_input_port_forward_request(s->pw->pw_uid == 0, options.gateway_ports); success = 1; break; case SSH_CMSG_MAX_PACKET_SIZE: if (packet_set_maxsize(packet_get_int()) > 0) success = 1; break; case SSH_CMSG_EXEC_SHELL: case SSH_CMSG_EXEC_CMD: if (type == SSH_CMSG_EXEC_CMD) { command = packet_get_string(&dlen); debug("Exec command '%.500s'", command); do_exec(s, command); xfree(command); } else { do_exec(s, NULL); } packet_check_eom(); session_close(s); return; default: /* * Any unknown messages in this phase are ignored, * and a failure message is returned. */ logit("Unknown packet type received after authentication: %d", type); } packet_start(success ? SSH_SMSG_SUCCESS : SSH_SMSG_FAILURE); packet_send(); packet_write_wait(); /* Enable compression now that we have replied if appropriate. */ if (enable_compression_after_reply) { enable_compression_after_reply = 0; packet_start_compression(compression_level); } } } /* * This is called to fork and execute a command when we have no tty. This * will call do_child from the child, and server_loop from the parent after * setting up file descriptors and such. */ void do_exec_no_pty(Session *s, const char *command) { pid_t pid; #ifdef USE_PIPES int pin[2], pout[2], perr[2]; /* Allocate pipes for communicating with the program. */ if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0) packet_disconnect("Could not create pipes: %.100s", strerror(errno)); #else /* USE_PIPES */ int inout[2], err[2]; /* Uses socket pairs to communicate with the program. */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 || socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) packet_disconnect("Could not create socket pairs: %.100s", strerror(errno)); #endif /* USE_PIPES */ if (s == NULL) fatal("do_exec_no_pty: no session"); session_proctitle(s); #if defined(USE_PAM) if (options.use_pam && !use_privsep) do_pam_setcred(1); #endif /* USE_PAM */ /* Fork the child. */ if ((pid = fork()) == 0) { is_child = 1; /* Child. Reinitialize the log since the pid has changed. */ log_init(__progname, options.log_level, options.log_facility, log_stderr); /* * Create a new session and process group since the 4.4BSD * setlogin() affects the entire process group. */ if (setsid() < 0) error("setsid failed: %.100s", strerror(errno)); #ifdef USE_PIPES /* * Redirect stdin. We close the parent side of the socket * pair, and make the child side the standard input. */ close(pin[1]); if (dup2(pin[0], 0) < 0) perror("dup2 stdin"); close(pin[0]); /* Redirect stdout. */ close(pout[0]); if (dup2(pout[1], 1) < 0) perror("dup2 stdout"); close(pout[1]); /* Redirect stderr. */ close(perr[0]); if (dup2(perr[1], 2) < 0) perror("dup2 stderr"); close(perr[1]); #else /* USE_PIPES */ /* * Redirect stdin, stdout, and stderr. Stdin and stdout will * use the same socket, as some programs (particularly rdist) * seem to depend on it. */ close(inout[1]); close(err[1]); if (dup2(inout[0], 0) < 0) /* stdin */ perror("dup2 stdin"); if (dup2(inout[0], 1) < 0) /* stdout. Note: same socket as stdin. */ perror("dup2 stdout"); if (dup2(err[0], 2) < 0) /* stderr */ perror("dup2 stderr"); #endif /* USE_PIPES */ #ifdef _UNICOS cray_init_job(s->pw); /* set up cray jid and tmpdir */ #endif /* Do processing for the child (exec command etc). */ do_child(s, command); /* NOTREACHED */ } #ifdef _UNICOS signal(WJSIGNAL, cray_job_termination_handler); #endif /* _UNICOS */ #ifdef HAVE_CYGWIN if (is_winnt) cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); #endif if (pid < 0) packet_disconnect("fork failed: %.100s", strerror(errno)); s->pid = pid; /* Set interactive/non-interactive mode. */ packet_set_interactive(s->display != NULL); #ifdef USE_PIPES /* We are the parent. Close the child sides of the pipes. */ close(pin[0]); close(pout[1]); close(perr[1]); if (compat20) { if (s->is_subsystem) { close(perr[0]); perr[0] = -1; } session_set_fds(s, pin[1], pout[0], perr[0]); } else { /* Enter the interactive session. */ server_loop(pid, pin[1], pout[0], perr[0]); /* server_loop has closed pin[1], pout[0], and perr[0]. */ } #else /* USE_PIPES */ /* We are the parent. Close the child sides of the socket pairs. */ close(inout[0]); close(err[0]); /* * Clear loginmsg, since it's the child's responsibility to display * it to the user, otherwise multiple sessions may accumulate * multiple copies of the login messages. */ buffer_clear(&loginmsg); /* * Enter the interactive session. Note: server_loop must be able to * handle the case that fdin and fdout are the same. */ if (compat20) { session_set_fds(s, inout[1], inout[1], s->is_subsystem ? -1 : err[1]); } else { server_loop(pid, inout[1], inout[1], err[1]); /* server_loop has closed inout[1] and err[1]. */ } #endif /* USE_PIPES */ } /* * This is called to fork and execute a command when we have a tty. This * will call do_child from the child, and server_loop from the parent after * setting up file descriptors, controlling tty, updating wtmp, utmp, * lastlog, and other such operations. */ void do_exec_pty(Session *s, const char *command) { int fdout, ptyfd, ttyfd, ptymaster; pid_t pid; if (s == NULL) fatal("do_exec_pty: no session"); ptyfd = s->ptyfd; ttyfd = s->ttyfd; #if defined(USE_PAM) if (options.use_pam) { do_pam_set_tty(s->tty); if (!use_privsep) do_pam_setcred(1); } #endif /* Fork the child. */ if ((pid = fork()) == 0) { is_child = 1; /* Child. Reinitialize the log because the pid has changed. */ log_init(__progname, options.log_level, options.log_facility, log_stderr); /* Close the master side of the pseudo tty. */ close(ptyfd); /* Make the pseudo tty our controlling tty. */ pty_make_controlling_tty(&ttyfd, s->tty); /* Redirect stdin/stdout/stderr from the pseudo tty. */ if (dup2(ttyfd, 0) < 0) error("dup2 stdin: %s", strerror(errno)); if (dup2(ttyfd, 1) < 0) error("dup2 stdout: %s", strerror(errno)); if (dup2(ttyfd, 2) < 0) error("dup2 stderr: %s", strerror(errno)); /* Close the extra descriptor for the pseudo tty. */ close(ttyfd); /* record login, etc. similar to login(1) */ #ifndef HAVE_OSF_SIA if (!(options.use_login && command == NULL)) { #ifdef _UNICOS cray_init_job(s->pw); /* set up cray jid and tmpdir */ #endif /* _UNICOS */ do_login(s, command); } # ifdef LOGIN_NEEDS_UTMPX else do_pre_login(s); # endif #endif /* Do common processing for the child, such as execing the command. */ do_child(s, command); /* NOTREACHED */ } #ifdef _UNICOS signal(WJSIGNAL, cray_job_termination_handler); #endif /* _UNICOS */ #ifdef HAVE_CYGWIN if (is_winnt) cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); #endif if (pid < 0) packet_disconnect("fork failed: %.100s", strerror(errno)); s->pid = pid; /* Parent. Close the slave side of the pseudo tty. */ close(ttyfd); /* * Create another descriptor of the pty master side for use as the * standard input. We could use the original descriptor, but this * simplifies code in server_loop. The descriptor is bidirectional. */ fdout = dup(ptyfd); if (fdout < 0) packet_disconnect("dup #1 failed: %.100s", strerror(errno)); /* we keep a reference to the pty master */ ptymaster = dup(ptyfd); if (ptymaster < 0) packet_disconnect("dup #2 failed: %.100s", strerror(errno)); s->ptymaster = ptymaster; /* Enter interactive session. */ packet_set_interactive(1); if (compat20) { session_set_fds(s, ptyfd, fdout, -1); } else { server_loop(pid, ptyfd, fdout, -1); /* server_loop _has_ closed ptyfd and fdout. */ } } #ifdef LOGIN_NEEDS_UTMPX static void do_pre_login(Session *s) { socklen_t fromlen; struct sockaddr_storage from; pid_t pid = getpid(); /* * Get IP address of client. If the connection is not a socket, let * the address be 0.0.0.0. */ memset(&from, 0, sizeof(from)); fromlen = sizeof(from); if (packet_connection_is_on_socket()) { if (getpeername(packet_get_connection_in(), (struct sockaddr *) & from, &fromlen) < 0) { debug("getpeername: %.100s", strerror(errno)); cleanup_exit(255); } } record_utmp_only(pid, s->tty, s->pw->pw_name, get_remote_name_or_ip(utmp_len, options.use_dns), (struct sockaddr *)&from, fromlen); } #endif /* * This is called to fork and execute a command. If another command is * to be forced, execute that instead. */ void do_exec(Session *s, const char *command) { if (forced_command) { original_command = command; command = forced_command; debug("Forced command '%.900s'", command); } #ifdef GSSAPI if (options.gss_authentication) { temporarily_use_uid(s->pw); ssh_gssapi_storecreds(); restore_uid(); } #endif if (s->ttyfd != -1) do_exec_pty(s, command); else do_exec_no_pty(s, command); original_command = NULL; /* * Clear loginmsg: it's the child's responsibility to display * it to the user, otherwise multiple sessions may accumulate * multiple copies of the login messages. */ buffer_clear(&loginmsg); } /* administrative, login(1)-like work */ void do_login(Session *s, const char *command) { socklen_t fromlen; struct sockaddr_storage from; struct passwd * pw = s->pw; pid_t pid = getpid(); /* * Get IP address of client. If the connection is not a socket, let * the address be 0.0.0.0. */ memset(&from, 0, sizeof(from)); fromlen = sizeof(from); if (packet_connection_is_on_socket()) { if (getpeername(packet_get_connection_in(), (struct sockaddr *) & from, &fromlen) < 0) { debug("getpeername: %.100s", strerror(errno)); cleanup_exit(255); } } /* Record that there was a login on that tty from the remote host. */ if (!use_privsep) record_login(pid, s->tty, pw->pw_name, pw->pw_uid, get_remote_name_or_ip(utmp_len, options.use_dns), (struct sockaddr *)&from, fromlen); #ifdef USE_PAM /* * If password change is needed, do it now. * This needs to occur before the ~/.hushlogin check. */ if (options.use_pam && !use_privsep && s->authctxt->force_pwchange) { display_loginmsg(); do_pam_chauthtok(); s->authctxt->force_pwchange = 0; /* XXX - signal [net] parent to enable forwardings */ } #endif if (check_quietlogin(s, command)) return; display_loginmsg(); do_motd(); } /* * Display the message of the day. */ void do_motd(void) { FILE *f; char buf[256]; if (options.print_motd) { #ifdef HAVE_LOGIN_CAP f = fopen(login_getcapstr(lc, "welcome", "/etc/motd", "/etc/motd"), "r"); #else f = fopen("/etc/motd", "r"); #endif if (f) { while (fgets(buf, sizeof(buf), f)) fputs(buf, stdout); fclose(f); } } } /* * Check for quiet login, either .hushlogin or command given. */ int check_quietlogin(Session *s, const char *command) { char buf[256]; struct passwd *pw = s->pw; struct stat st; /* Return 1 if .hushlogin exists or a command given. */ if (command != NULL) return 1; snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir); #ifdef HAVE_LOGIN_CAP if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0) return 1; #else if (stat(buf, &st) >= 0) return 1; #endif return 0; } /* * Sets the value of the given variable in the environment. If the variable * already exists, its value is overriden. */ void child_set_env(char ***envp, u_int *envsizep, const char *name, const char *value) { char **env; u_int envsize; u_int i, namelen; /* * If we're passed an uninitialized list, allocate a single null * entry before continuing. */ if (*envp == NULL && *envsizep == 0) { *envp = xmalloc(sizeof(char *)); *envp[0] = NULL; *envsizep = 1; } /* * Find the slot where the value should be stored. If the variable * already exists, we reuse the slot; otherwise we append a new slot * at the end of the array, expanding if necessary. */ env = *envp; namelen = strlen(name); for (i = 0; env[i]; i++) if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=') break; if (env[i]) { /* Reuse the slot. */ xfree(env[i]); } else { /* New variable. Expand if necessary. */ envsize = *envsizep; if (i >= envsize - 1) { if (envsize >= 1000) fatal("child_set_env: too many env vars"); envsize += 50; env = (*envp) = xrealloc(env, envsize * sizeof(char *)); *envsizep = envsize; } /* Need to set the NULL pointer at end of array beyond the new slot. */ env[i + 1] = NULL; } /* Allocate space and format the variable in the appropriate slot. */ env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); } /* * Reads environment variables from the given file and adds/overrides them * into the environment. If the file does not exist, this does nothing. * Otherwise, it must consist of empty lines, comments (line starts with '#') * and assignments of the form name=value. No other forms are allowed. */ static void read_environment_file(char ***env, u_int *envsize, const char *filename) { FILE *f; char buf[4096]; char *cp, *value; u_int lineno = 0; f = fopen(filename, "r"); if (!f) return; while (fgets(buf, sizeof(buf), f)) { if (++lineno > 1000) fatal("Too many lines in environment file %s", filename); for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) ; if (!*cp || *cp == '#' || *cp == '\n') continue; if (strchr(cp, '\n')) *strchr(cp, '\n') = '\0'; value = strchr(cp, '='); if (value == NULL) { fprintf(stderr, "Bad line %u in %.100s\n", lineno, filename); continue; } /* * Replace the equals sign by nul, and advance value to * the value string. */ *value = '\0'; value++; child_set_env(env, envsize, cp, value); } fclose(f); } #ifdef HAVE_ETC_DEFAULT_LOGIN /* * Return named variable from specified environment, or NULL if not present. */ static char * child_get_env(char **env, const char *name) { int i; size_t len; len = strlen(name); for (i=0; env[i] != NULL; i++) if (strncmp(name, env[i], len) == 0 && env[i][len] == '=') return(env[i] + len + 1); return NULL; } /* * Read /etc/default/login. * We pick up the PATH (or SUPATH for root) and UMASK. */ static void read_etc_default_login(char ***env, u_int *envsize, uid_t uid) { char **tmpenv = NULL, *var; u_int i, tmpenvsize = 0; u_long mask; /* * We don't want to copy the whole file to the child's environment, * so we use a temporary environment and copy the variables we're * interested in. */ read_environment_file(&tmpenv, &tmpenvsize, "/etc/default/login"); if (tmpenv == NULL) return; if (uid == 0) var = child_get_env(tmpenv, "SUPATH"); else var = child_get_env(tmpenv, "PATH"); if (var != NULL) child_set_env(env, envsize, "PATH", var); if ((var = child_get_env(tmpenv, "UMASK")) != NULL) if (sscanf(var, "%5lo", &mask) == 1) umask((mode_t)mask); for (i = 0; tmpenv[i] != NULL; i++) xfree(tmpenv[i]); xfree(tmpenv); } #endif /* HAVE_ETC_DEFAULT_LOGIN */ void copy_environment(char **source, char ***env, u_int *envsize) { char *var_name, *var_val; int i; if (source == NULL) return; for(i = 0; source[i] != NULL; i++) { var_name = xstrdup(source[i]); if ((var_val = strstr(var_name, "=")) == NULL) { xfree(var_name); continue; } *var_val++ = '\0'; debug3("Copy environment: %s=%s", var_name, var_val); child_set_env(env, envsize, var_name, var_val); xfree(var_name); } } static char ** do_setup_env(Session *s, const char *shell) { char buf[256]; u_int i, envsize; char **env, *laddr, *path = NULL; struct passwd *pw = s->pw; /* Initialize the environment. */ envsize = 100; env = xmalloc(envsize * sizeof(char *)); env[0] = NULL; #ifdef HAVE_CYGWIN /* * The Windows environment contains some setting which are * important for a running system. They must not be dropped. */ copy_environment(environ, &env, &envsize); #endif #ifdef GSSAPI /* Allow any GSSAPI methods that we've used to alter * the childs environment as they see fit */ ssh_gssapi_do_child(&env, &envsize); #endif if (!options.use_login) { /* Set basic environment. */ for (i = 0; i < s->num_env; i++) child_set_env(&env, &envsize, s->env[i].name, s->env[i].val); child_set_env(&env, &envsize, "USER", pw->pw_name); child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); #ifdef _AIX child_set_env(&env, &envsize, "LOGIN", pw->pw_name); #endif child_set_env(&env, &envsize, "HOME", pw->pw_dir); #ifdef HAVE_LOGIN_CAP if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETPATH) < 0) child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); else child_set_env(&env, &envsize, "PATH", getenv("PATH")); #else /* HAVE_LOGIN_CAP */ # ifndef HAVE_CYGWIN /* * There's no standard path on Windows. The path contains * important components pointing to the system directories, * needed for loading shared libraries. So the path better * remains intact here. */ # ifdef HAVE_ETC_DEFAULT_LOGIN read_etc_default_login(&env, &envsize, pw->pw_uid); path = child_get_env(env, "PATH"); # endif /* HAVE_ETC_DEFAULT_LOGIN */ if (path == NULL || *path == '\0') { child_set_env(&env, &envsize, "PATH", s->pw->pw_uid == 0 ? SUPERUSER_PATH : _PATH_STDPATH); } # endif /* HAVE_CYGWIN */ #endif /* HAVE_LOGIN_CAP */ snprintf(buf, sizeof buf, "%.200s/%.50s", _PATH_MAILDIR, pw->pw_name); child_set_env(&env, &envsize, "MAIL", buf); #ifdef CONDOR_SSHD_XXX /* This can't be /bin/sh */ shell = "/bin/tcsh"; #endif /* Normal systems set SHELL by default. */ child_set_env(&env, &envsize, "SHELL", shell); } if (getenv("TZ")) child_set_env(&env, &envsize, "TZ", getenv("TZ")); /* Set custom environment options from RSA authentication. */ if (!options.use_login) { while (custom_environment) { struct envstring *ce = custom_environment; char *str = ce->s; for (i = 0; str[i] != '=' && str[i]; i++) ; if (str[i] == '=') { str[i] = 0; child_set_env(&env, &envsize, str, str + i + 1); } custom_environment = ce->next; xfree(ce->s); xfree(ce); } } /* SSH_CLIENT deprecated */ snprintf(buf, sizeof buf, "%.50s %d %d", get_remote_ipaddr(), get_remote_port(), get_local_port()); child_set_env(&env, &envsize, "SSH_CLIENT", buf); laddr = get_local_ipaddr(packet_get_connection_in()); snprintf(buf, sizeof buf, "%.50s %d %.50s %d", get_remote_ipaddr(), get_remote_port(), laddr, get_local_port()); xfree(laddr); child_set_env(&env, &envsize, "SSH_CONNECTION", buf); if (s->ttyfd != -1) child_set_env(&env, &envsize, "SSH_TTY", s->tty); if (s->term) child_set_env(&env, &envsize, "TERM", s->term); if (s->display) child_set_env(&env, &envsize, "DISPLAY", s->display); if (original_command) child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND", original_command); #ifdef _UNICOS if (cray_tmpdir[0] != '\0') child_set_env(&env, &envsize, "TMPDIR", cray_tmpdir); #endif /* _UNICOS */ #ifdef _AIX { char *cp; if ((cp = getenv("AUTHSTATE")) != NULL) child_set_env(&env, &envsize, "AUTHSTATE", cp); if ((cp = getenv("KRB5CCNAME")) != NULL) child_set_env(&env, &envsize, "KRB5CCNAME", cp); read_environment_file(&env, &envsize, "/etc/environment"); } #endif #ifdef KRB5 if (s->authctxt->krb5_ccname) child_set_env(&env, &envsize, "KRB5CCNAME", s->authctxt->krb5_ccname); #endif #ifdef USE_PAM /* * Pull in any environment variables that may have * been set by PAM. */ if (options.use_pam) { char **p; p = fetch_pam_child_environment(); copy_environment(p, &env, &envsize); free_pam_environment(p); p = fetch_pam_environment(); copy_environment(p, &env, &envsize); free_pam_environment(p); } #endif /* USE_PAM */ if (auth_sock_name != NULL) child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, auth_sock_name); /* read $HOME/.ssh/environment. */ if (options.permit_user_env && !options.use_login) { snprintf(buf, sizeof buf, "%.200s/.ssh/environment", strcmp(pw->pw_dir, "/") ? pw->pw_dir : ""); read_environment_file(&env, &envsize, buf); } if (debug_flag) { /* dump the environment */ fprintf(stderr, "Environment:\n"); for (i = 0; env[i]; i++) fprintf(stderr, " %.200s\n", env[i]); } return env; } /* * Run $HOME/.ssh/rc, /etc/ssh/sshrc, or xauth (whichever is found * first in this order). */ static void do_rc_files(Session *s, const char *shell) { FILE *f = NULL; char cmd[1024]; int do_xauth; struct stat st; do_xauth = s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL; /* ignore _PATH_SSH_USER_RC for subsystems */ if (!s->is_subsystem && (stat(_PATH_SSH_USER_RC, &st) >= 0)) { snprintf(cmd, sizeof cmd, "%s -c '%s %s'", shell, _PATH_BSHELL, _PATH_SSH_USER_RC); if (debug_flag) fprintf(stderr, "Running %s\n", cmd); f = popen(cmd, "w"); if (f) { if (do_xauth) fprintf(f, "%s %s\n", s->auth_proto, s->auth_data); pclose(f); } else fprintf(stderr, "Could not run %s\n", _PATH_SSH_USER_RC); } else if (stat(_PATH_SSH_SYSTEM_RC, &st) >= 0) { if (debug_flag) fprintf(stderr, "Running %s %s\n", _PATH_BSHELL, _PATH_SSH_SYSTEM_RC); f = popen(_PATH_BSHELL " " _PATH_SSH_SYSTEM_RC, "w"); if (f) { if (do_xauth) fprintf(f, "%s %s\n", s->auth_proto, s->auth_data); pclose(f); } else fprintf(stderr, "Could not run %s\n", _PATH_SSH_SYSTEM_RC); } else if (do_xauth && options.xauth_location != NULL) { /* Add authority data to .Xauthority if appropriate. */ if (debug_flag) { fprintf(stderr, "Running %.500s remove %.100s\n", options.xauth_location, s->auth_display); fprintf(stderr, "%.500s add %.100s %.100s %.100s\n", options.xauth_location, s->auth_display, s->auth_proto, s->auth_data); } snprintf(cmd, sizeof cmd, "%s -q -", options.xauth_location); f = popen(cmd, "w"); if (f) { fprintf(f, "remove %s\n", s->auth_display); fprintf(f, "add %s %s %s\n", s->auth_display, s->auth_proto, s->auth_data); pclose(f); } else { fprintf(stderr, "Could not run %s\n", cmd); } } } static void do_nologin(struct passwd *pw) { FILE *f = NULL; char buf[1024]; #ifdef HAVE_LOGIN_CAP if (!login_getcapbool(lc, "ignorenologin", 0) && pw->pw_uid) f = fopen(login_getcapstr(lc, "nologin", _PATH_NOLOGIN, _PATH_NOLOGIN), "r"); #else if (pw->pw_uid) f = fopen(_PATH_NOLOGIN, "r"); #endif if (f) { /* /etc/nologin exists. Print its contents and exit. */ logit("User %.100s not allowed because %s exists", pw->pw_name, _PATH_NOLOGIN); while (fgets(buf, sizeof(buf), f)) fputs(buf, stderr); fclose(f); fflush(NULL); exit(254); } } /* Set login name, uid, gid, and groups. */ void do_setusercontext(struct passwd *pw) { #ifndef HAVE_CYGWIN if (getuid() == 0 || geteuid() == 0) #endif /* HAVE_CYGWIN */ { #ifdef HAVE_SETPCRED if (setpcred(pw->pw_name, (char **)NULL) == -1) fatal("Failed to set process credentials"); #endif /* HAVE_SETPCRED */ #ifdef HAVE_LOGIN_CAP # ifdef __bsdi__ setpgid(0, 0); # endif # ifdef USE_PAM if (options.use_pam) { do_pam_session(); do_pam_setcred(0); } # endif /* USE_PAM */ if (setusercontext(lc, pw, pw->pw_uid, (LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) { perror("unable to set user context"); exit(1); } #else # if defined(HAVE_GETLUID) && defined(HAVE_SETLUID) /* Sets login uid for accounting */ if (getluid() == -1 && setluid(pw->pw_uid) == -1) error("setluid: %s", strerror(errno)); # endif /* defined(HAVE_GETLUID) && defined(HAVE_SETLUID) */ if (setlogin(pw->pw_name) < 0) error("setlogin failed: %s", strerror(errno)); if (setgid(pw->pw_gid) < 0) { perror("setgid"); exit(1); } /* Initialize the group list. */ if (initgroups(pw->pw_name, pw->pw_gid) < 0) { perror("initgroups"); exit(1); } endgrent(); # ifdef USE_PAM /* * PAM credentials may take the form of supplementary groups. * These will have been wiped by the above initgroups() call. * Reestablish them here. */ if (options.use_pam) { do_pam_session(); do_pam_setcred(0); } # endif /* USE_PAM */ # if defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) irix_setusercontext(pw); # endif /* defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) */ # ifdef _AIX aix_usrinfo(pw); # endif /* _AIX */ /* Permanently switch to the desired uid. */ permanently_set_uid(pw); #endif } #ifdef HAVE_CYGWIN if (is_winnt) #endif if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) fatal("Failed to set uids to %u.", (u_int) pw->pw_uid); } static void do_pwchange(Session *s) { fflush(NULL); fprintf(stderr, "WARNING: Your password has expired.\n"); if (s->ttyfd != -1) { fprintf(stderr, "You must change your password now and login again!\n"); execl(_PATH_PASSWD_PROG, "passwd", (char *)NULL); perror("passwd"); } else { fprintf(stderr, "Password change required but no TTY available.\n"); } exit(1); } static void launch_login(struct passwd *pw, const char *hostname) { /* Launch login(1). */ execl(LOGIN_PROGRAM, "login", "-h", hostname, #ifdef xxxLOGIN_NEEDS_TERM (s->term ? s->term : "unknown"), #endif /* LOGIN_NEEDS_TERM */ #ifdef LOGIN_NO_ENDOPT "-p", "-f", pw->pw_name, (char *)NULL); #else "-p", "-f", "--", pw->pw_name, (char *)NULL); #endif /* Login couldn't be executed, die. */ perror("login"); exit(1); } static void child_close_fds(void) { int i; if (packet_get_connection_in() == packet_get_connection_out()) close(packet_get_connection_in()); else { close(packet_get_connection_in()); close(packet_get_connection_out()); } /* * Close all descriptors related to channels. They will still remain * open in the parent. */ /* XXX better use close-on-exec? -markus */ channel_close_all(); /* * Close any extra file descriptors. Note that there may still be * descriptors left by system functions. They will be closed later. */ endpwent(); /* * Close any extra open file descriptors so that we don\'t have them * hanging around in clients. Note that we want to do this after * initgroups, because at least on Solaris 2.3 it leaves file * descriptors open. */ for (i = 3; i < 64; i++) close(i); } /* * Performs common processing for the child, such as setting up the * environment, closing extra file descriptors, setting the user and group * ids, and executing the command or shell. */ void do_child(Session *s, const char *command) { extern char **environ; char **env; char *argv[10]; const char *shell, *shell0, *hostname = NULL; struct passwd *pw = s->pw; /* remove hostkey from the child's memory */ destroy_sensitive_data(); /* Force a password change */ if (s->authctxt->force_pwchange) { do_setusercontext(pw); child_close_fds(); do_pwchange(s); exit(1); } /* login(1) is only called if we execute the login shell */ if (options.use_login && command != NULL) options.use_login = 0; #ifdef _UNICOS cray_setup(pw->pw_uid, pw->pw_name, command); #endif /* _UNICOS */ /* * Login(1) does this as well, and it needs uid 0 for the "-h" * switch, so we let login(1) to this for us. */ if (!options.use_login) { #ifdef HAVE_OSF_SIA session_setup_sia(pw, s->ttyfd == -1 ? NULL : s->tty); if (!check_quietlogin(s, command)) do_motd(); #else /* HAVE_OSF_SIA */ do_nologin(pw); do_setusercontext(pw); /* * PAM session modules in do_setusercontext may have * generated messages, so if this in an interactive * login then display them too. */ if (command == NULL) display_loginmsg(); #endif /* HAVE_OSF_SIA */ } /* * Get the shell from the password data. An empty shell field is * legal, and means /bin/sh. */ shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; #ifdef CONDOR_SSHD shell = "/bin/tcsh"; #endif /* * Make sure $SHELL points to the shell from the password file, * even if shell is overridden from login.conf */ env = do_setup_env(s, shell); #ifdef HAVE_LOGIN_CAP shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell); #endif /* we have to stash the hostname before we close our socket. */ if (options.use_login) hostname = get_remote_name_or_ip(utmp_len, options.use_dns); /* * Close the connection descriptors; note that this is the child, and * the server will still have the socket open, and it is important * that we do not shutdown it. Note that the descriptors cannot be * closed before building the environment, as we call * get_remote_ipaddr there. */ child_close_fds(); /* * Must take new environment into use so that .ssh/rc, * /etc/ssh/sshrc and xauth are run in the proper environment. */ environ = env; #if defined(KRB5) && defined(USE_AFS) /* * At this point, we check to see if AFS is active and if we have * a valid Kerberos 5 TGT. If so, it seems like a good idea to see * if we can (and need to) extend the ticket into an AFS token. If * we don't do this, we run into potential problems if the user's * home directory is in AFS and it's not world-readable. */ if (options.kerberos_get_afs_token && k_hasafs() && (s->authctxt->krb5_ctx != NULL)) { char cell[64]; debug("Getting AFS token"); k_setpag(); if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0) krb5_afslog(s->authctxt->krb5_ctx, s->authctxt->krb5_fwd_ccache, cell, NULL); krb5_afslog_home(s->authctxt->krb5_ctx, s->authctxt->krb5_fwd_ccache, NULL, NULL, pw->pw_dir); } #endif /* * Under condor, we dont want to cd, or even try to run the users * shell (which might not exist, if we are using ephemeral * uids. */ #ifndef CONDOR_SSHD /* Change current directory to the user\'s home directory. */ if (chdir(pw->pw_dir) < 0) { fprintf(stderr, "Could not chdir to home directory %s: %s\n", pw->pw_dir, strerror(errno)); #ifdef HAVE_LOGIN_CAP if (login_getcapbool(lc, "requirehome", 0)) exit(1); #endif } #else shell = "/bin/tcsh"; #endif if (!options.use_login) do_rc_files(s, shell); /* restore SIGPIPE for child */ signal(SIGPIPE, SIG_DFL); if (options.use_login) { launch_login(pw, hostname); /* NEVERREACHED */ } /* Get the last component of the shell name. */ if ((shell0 = strrchr(shell, '/')) != NULL) shell0++; else shell0 = shell; /* * If we have no command, execute the shell. In this case, the shell * name to be passed in argv[0] is preceded by '-' to indicate that * this is a login shell. */ if (!command) { char argv0[256]; /* Start the shell. Set initial character to '-'. */ argv0[0] = '-'; if (strlcpy(argv0 + 1, shell0, sizeof(argv0) - 1) >= sizeof(argv0) - 1) { errno = EINVAL; perror(shell); exit(1); } /* Execute the shell. */ argv[0] = argv0; argv[1] = NULL; execve(shell, argv, env); /* Executing the shell failed. */ perror(shell); exit(1); } /* * Execute the command using the user's shell. This uses the -c * option to execute the command. */ argv[0] = (char *) shell0; argv[1] = "-c"; argv[2] = (char *) command; argv[3] = NULL; execve(shell, argv, env); perror(shell); exit(1); } Session * session_new(void) { int i; static int did_init = 0; if (!did_init) { debug("session_new: init"); for (i = 0; i < MAX_SESSIONS; i++) { sessions[i].used = 0; } did_init = 1; } for (i = 0; i < MAX_SESSIONS; i++) { Session *s = &sessions[i]; if (! s->used) { memset(s, 0, sizeof(*s)); s->chanid = -1; s->ptyfd = -1; s->ttyfd = -1; s->used = 1; s->self = i; debug("session_new: session %d", i); return s; } } return NULL; } static void session_dump(void) { int i; for (i = 0; i < MAX_SESSIONS; i++) { Session *s = &sessions[i]; debug("dump: used %d session %d %p channel %d pid %ld", s->used, s->self, s, s->chanid, (long)s->pid); } } int session_open(Authctxt *authctxt, int chanid) { Session *s = session_new(); debug("session_open: channel %d", chanid); if (s == NULL) { error("no more sessions"); return 0; } s->authctxt = authctxt; s->pw = authctxt->pw; if (s->pw == NULL || !authctxt->valid) fatal("no user for session %d", s->self); debug("session_open: session %d: link with channel %d", s->self, chanid); s->chanid = chanid; return 1; } Session * session_by_tty(char *tty) { int i; for (i = 0; i < MAX_SESSIONS; i++) { Session *s = &sessions[i]; if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) { debug("session_by_tty: session %d tty %s", i, tty); return s; } } debug("session_by_tty: unknown tty %.100s", tty); session_dump(); return NULL; } static Session * session_by_channel(int id) { int i; for (i = 0; i < MAX_SESSIONS; i++) { Session *s = &sessions[i]; if (s->used && s->chanid == id) { debug("session_by_channel: session %d channel %d", i, id); return s; } } debug("session_by_channel: unknown channel %d", id); session_dump(); return NULL; } static Session * session_by_pid(pid_t pid) { int i; debug("session_by_pid: pid %ld", (long)pid); for (i = 0; i < MAX_SESSIONS; i++) { Session *s = &sessions[i]; if (s->used && s->pid == pid) return s; } error("session_by_pid: unknown pid %ld", (long)pid); session_dump(); return NULL; } static int session_window_change_req(Session *s) { s->col = packet_get_int(); s->row = packet_get_int(); s->xpixel = packet_get_int(); s->ypixel = packet_get_int(); packet_check_eom(); pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); return 1; } static int session_pty_req(Session *s) { u_int len; int n_bytes; if (no_pty_flag) { debug("Allocating a pty not permitted for this authentication."); return 0; } if (s->ttyfd != -1) { packet_disconnect("Protocol error: you already have a pty."); return 0; } s->term = packet_get_string(&len); if (compat20) { s->col = packet_get_int(); s->row = packet_get_int(); } else { s->row = packet_get_int(); s->col = packet_get_int(); } s->xpixel = packet_get_int(); s->ypixel = packet_get_int(); if (strcmp(s->term, "") == 0) { xfree(s->term); s->term = NULL; } /* Allocate a pty and open it. */ debug("Allocating pty."); if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)))) { if (s->term) xfree(s->term); s->term = NULL; s->ptyfd = -1; s->ttyfd = -1; error("session_pty_req: session %d alloc failed", s->self); return 0; } debug("session_pty_req: session %d alloc %s", s->self, s->tty); /* for SSH1 the tty modes length is not given */ if (!compat20) n_bytes = packet_remaining(); tty_parse_modes(s->ttyfd, &n_bytes); if (!use_privsep) pty_setowner(s->pw, s->tty); /* Set window size from the packet. */ pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); packet_check_eom(); session_proctitle(s); return 1; } static int session_subsystem_req(Session *s) { struct stat st; u_int len; int success = 0; char *cmd, *subsys = packet_get_string(&len); int i; packet_check_eom(); logit("subsystem request for %.100s", subsys); for (i = 0; i < options.num_subsystems; i++) { if (strcmp(subsys, options.subsystem_name[i]) == 0) { cmd = options.subsystem_command[i]; if (stat(cmd, &st) < 0) { error("subsystem: cannot stat %s: %s", cmd, strerror(errno)); break; } debug("subsystem: exec() %s", cmd); s->is_subsystem = 1; do_exec(s, cmd); success = 1; break; } } if (!success) logit("subsystem request for %.100s failed, subsystem not found", subsys); xfree(subsys); return success; } static int session_x11_req(Session *s) { int success; s->single_connection = packet_get_char(); s->auth_proto = packet_get_string(NULL); s->auth_data = packet_get_string(NULL); s->screen = packet_get_int(); packet_check_eom(); success = session_setup_x11fwd(s); if (!success) { xfree(s->auth_proto); xfree(s->auth_data); s->auth_proto = NULL; s->auth_data = NULL; } return success; } static int session_shell_req(Session *s) { packet_check_eom(); do_exec(s, NULL); return 1; } static int session_exec_req(Session *s) { u_int len; char *command = packet_get_string(&len); packet_check_eom(); do_exec(s, command); xfree(command); return 1; } static int session_break_req(Session *s) { packet_get_int(); /* ignored */ packet_check_eom(); if (s->ttyfd == -1 || tcsendbreak(s->ttyfd, 0) < 0) return 0; return 1; } static int session_env_req(Session *s) { char *name, *val; u_int name_len, val_len, i; name = packet_get_string(&name_len); val = packet_get_string(&val_len); packet_check_eom(); /* Don't set too many environment variables */ if (s->num_env > 128) { debug2("Ignoring env request %s: too many env vars", name); goto fail; } for (i = 0; i < options.num_accept_env; i++) { if (match_pattern(name, options.accept_env[i])) { debug2("Setting env %d: %s=%s", s->num_env, name, val); s->env = xrealloc(s->env, sizeof(*s->env) * (s->num_env + 1)); s->env[s->num_env].name = name; s->env[s->num_env].val = val; s->num_env++; return (1); } } debug2("Ignoring env request %s: disallowed name", name); fail: xfree(name); xfree(val); return (0); } static int session_auth_agent_req(Session *s) { static int called = 0; packet_check_eom(); if (no_agent_forwarding_flag) { debug("session_auth_agent_req: no_agent_forwarding_flag"); return 0; } if (called) { return 0; } else { called = 1; return auth_input_request_forwarding(s->pw); } } int session_input_channel_req(Channel *c, const char *rtype) { int success = 0; Session *s; if ((s = session_by_channel(c->self)) == NULL) { logit("session_input_channel_req: no session %d req %.100s", c->self, rtype); return 0; } debug("session_input_channel_req: session %d req %s", s->self, rtype); /* * a session is in LARVAL state until a shell, a command * or a subsystem is executed */ if (c->type == SSH_CHANNEL_LARVAL) { if (strcmp(rtype, "shell") == 0) { success = session_shell_req(s); } else if (strcmp(rtype, "exec") == 0) { success = session_exec_req(s); } else if (strcmp(rtype, "pty-req") == 0) { success = session_pty_req(s); } else if (strcmp(rtype, "x11-req") == 0) { success = session_x11_req(s); } else if (strcmp(rtype, "auth-agent-req@xxxxxxxxxxx") == 0) { success = session_auth_agent_req(s); } else if (strcmp(rtype, "subsystem") == 0) { success = session_subsystem_req(s); } else if (strcmp(rtype, "env") == 0) { success = session_env_req(s); } } if (strcmp(rtype, "window-change") == 0) { success = session_window_change_req(s); } else if (strcmp(rtype, "break") == 0) { success = session_break_req(s); } return success; } void session_set_fds(Session *s, int fdin, int fdout, int fderr) { if (!compat20) fatal("session_set_fds: called for proto != 2.0"); /* * now that have a child and a pipe to the child, * we can activate our channel and register the fd's */ if (s->chanid == -1) fatal("no channel for session %d", s->self); channel_set_fds(s->chanid, fdout, fdin, fderr, fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, 1, CHAN_SES_WINDOW_DEFAULT); } /* * Function to perform pty cleanup. Also called if we get aborted abnormally * (e.g., due to a dropped connection). */ void session_pty_cleanup2(Session *s) { if (s == NULL) { error("session_pty_cleanup: no session"); return; } if (s->ttyfd == -1) return; debug("session_pty_cleanup: session %d release %s", s->self, s->tty); /* Record that the user has logged out. */ if (s->pid != 0) record_logout(s->pid, s->tty, s->pw->pw_name); /* Release the pseudo-tty. */ if (getuid() == 0) pty_release(s->tty); /* * Close the server side of the socket pairs. We must do this after * the pty cleanup, so that another process doesn't get this pty * while we're still cleaning up. */ if (close(s->ptymaster) < 0) error("close(s->ptymaster/%d): %s", s->ptymaster, strerror(errno)); /* unlink pty from session */ s->ttyfd = -1; } void session_pty_cleanup(Session *s) { PRIVSEP(session_pty_cleanup2(s)); } static char * sig2name(int sig) { #define SSH_SIG(x) if (sig == SIG ## x) return #x SSH_SIG(ABRT); SSH_SIG(ALRM); SSH_SIG(FPE); SSH_SIG(HUP); SSH_SIG(ILL); SSH_SIG(INT); SSH_SIG(KILL); SSH_SIG(PIPE); SSH_SIG(QUIT); SSH_SIG(SEGV); SSH_SIG(TERM); SSH_SIG(USR1); SSH_SIG(USR2); #undef SSH_SIG return "SIG@xxxxxxxxxxx"; } static void session_exit_message(Session *s, int status) { Channel *c; if ((c = channel_lookup(s->chanid)) == NULL) fatal("session_exit_message: session %d: no channel %d", s->self, s->chanid); debug("session_exit_message: session %d channel %d pid %ld", s->self, s->chanid, (long)s->pid); if (WIFEXITED(status)) { channel_request_start(s->chanid, "exit-status", 0); packet_put_int(WEXITSTATUS(status)); packet_send(); } else if (WIFSIGNALED(status)) { channel_request_start(s->chanid, "exit-signal", 0); packet_put_cstring(sig2name(WTERMSIG(status))); #ifdef WCOREDUMP packet_put_char(WCOREDUMP(status)); #else /* WCOREDUMP */ packet_put_char(0); #endif /* WCOREDUMP */ packet_put_cstring(""); packet_put_cstring(""); packet_send(); } else { /* Some weird exit cause. Just exit. */ packet_disconnect("wait returned status %04x.", status); } /* disconnect channel */ debug("session_exit_message: release channel %d", s->chanid); channel_cancel_cleanup(s->chanid); /* * emulate a write failure with 'chan_write_failed', nobody will be * interested in data we write. * Note that we must not call 'chan_read_failed', since there could * be some more data waiting in the pipe. */ if (c->ostate != CHAN_OUTPUT_CLOSED) chan_write_failed(c); s->chanid = -1; } void session_close(Session *s) { int i; debug("session_close: session %d pid %ld", s->self, (long)s->pid); if (s->ttyfd != -1) session_pty_cleanup(s); if (s->term) xfree(s->term); if (s->display) xfree(s->display); if (s->auth_display) xfree(s->auth_display); if (s->auth_data) xfree(s->auth_data); if (s->auth_proto) xfree(s->auth_proto); s->used = 0; for (i = 0; i < s->num_env; i++) { xfree(s->env[i].name); xfree(s->env[i].val); } if (s->env != NULL) xfree(s->env); session_proctitle(s); } void session_close_by_pid(pid_t pid, int status) { Session *s = session_by_pid(pid); if (s == NULL) { debug("session_close_by_pid: no session for pid %ld", (long)pid); return; } if (s->chanid != -1) session_exit_message(s, status); session_close(s); } /* * this is called when a channel dies before * the session 'child' itself dies */ void session_close_by_channel(int id, void *arg) { Session *s = session_by_channel(id); if (s == NULL) { debug("session_close_by_channel: no session for id %d", id); return; } debug("session_close_by_channel: channel %d child %ld", id, (long)s->pid); if (s->pid != 0) { debug("session_close_by_channel: channel %d: has child", id); /* * delay detach of session, but release pty, since * the fd's to the child are already closed */ if (s->ttyfd != -1) session_pty_cleanup(s); return; } /* detach by removing callback */ channel_cancel_cleanup(s->chanid); s->chanid = -1; session_close(s); } void session_destroy_all(void (*closefunc)(Session *)) { int i; for (i = 0; i < MAX_SESSIONS; i++) { Session *s = &sessions[i]; if (s->used) { if (closefunc != NULL) closefunc(s); else session_close(s); } } } static char * session_tty_list(void) { static char buf[1024]; int i; char *cp; buf[0] = '\0'; for (i = 0; i < MAX_SESSIONS; i++) { Session *s = &sessions[i]; if (s->used && s->ttyfd != -1) { if (strncmp(s->tty, "/dev/", 5) != 0) { cp = strrchr(s->tty, '/'); cp = (cp == NULL) ? s->tty : cp + 1; } else cp = s->tty + 5; if (buf[0] != '\0') strlcat(buf, ",", sizeof buf); strlcat(buf, cp, sizeof buf); } } if (buf[0] == '\0') strlcpy(buf, "notty", sizeof buf); return buf; } void session_proctitle(Session *s) { if (s->pw == NULL) error("no user for session %d", s->self); else setproctitle("%s@%s", s->pw->pw_name, session_tty_list()); } int session_setup_x11fwd(Session *s) { struct stat st; char display[512], auth_display[512]; char hostname[MAXHOSTNAMELEN]; if (no_x11_forwarding_flag) { packet_send_debug("X11 forwarding disabled in user configuration file."); return 0; } if (!options.x11_forwarding) { debug("X11 forwarding disabled in server configuration file."); return 0; } if (!options.xauth_location || (stat(options.xauth_location, &st) == -1)) { packet_send_debug("No xauth program; cannot forward with spoofing."); return 0; } if (options.use_login) { packet_send_debug("X11 forwarding disabled; " "not compatible with UseLogin=yes."); return 0; } if (s->display != NULL) { debug("X11 display already set."); return 0; } if (x11_create_display_inet(options.x11_display_offset, options.x11_use_localhost, s->single_connection, &s->display_number) == -1) { debug("x11_create_display_inet failed."); return 0; } /* Set up a suitable value for the DISPLAY variable. */ if (gethostname(hostname, sizeof(hostname)) < 0) fatal("gethostname: %.100s", strerror(errno)); /* * auth_display must be used as the displayname when the * authorization entry is added with xauth(1). This will be * different than the DISPLAY string for localhost displays. */ if (options.x11_use_localhost) { snprintf(display, sizeof display, "localhost:%u.%u", s->display_number, s->screen); snprintf(auth_display, sizeof auth_display, "unix:%u.%u", s->display_number, s->screen); s->display = xstrdup(display); s->auth_display = xstrdup(auth_display); } else { #ifdef IPADDR_IN_DISPLAY struct hostent *he; struct in_addr my_addr; he = gethostbyname(hostname); if (he == NULL) { error("Can't get IP address for X11 DISPLAY."); packet_send_debug("Can't get IP address for X11 DISPLAY."); return 0; } memcpy(&my_addr, he->h_addr_list[0], sizeof(struct in_addr)); snprintf(display, sizeof display, "%.50s:%u.%u", inet_ntoa(my_addr), s->display_number, s->screen); #else snprintf(display, sizeof display, "%.400s:%u.%u", hostname, s->display_number, s->screen); #endif s->display = xstrdup(display); s->auth_display = xstrdup(display); } return 1; } static void do_authenticated2(Authctxt *authctxt) { server_loop2(authctxt); } void do_cleanup(Authctxt *authctxt) { static int called = 0; debug("do_cleanup"); /* no cleanup if we're in the child for login shell */ if (is_child) return; /* avoid double cleanup */ if (called) return; called = 1; if (authctxt == NULL) return; #ifdef KRB5 if (options.kerberos_ticket_cleanup && authctxt->krb5_ctx) krb5_cleanup_proc(authctxt); #endif #ifdef GSSAPI if (compat20 && options.gss_cleanup_creds) ssh_gssapi_cleanup_creds(); #endif #ifdef USE_PAM if (options.use_pam) { sshpam_cleanup(); sshpam_thread_cleanup(); } #endif /* remove agent socket */ auth_sock_cleanup_proc(authctxt->pw); /* * Cleanup ptys/utmp only if privsep is disabled, * or if running in monitor. */ if (!use_privsep || mm_is_monitor()) session_destroy_all(session_pty_cleanup2); }
/* * Author: Tatu Ylonen <ylo@xxxxxxxxx> * Copyright (c) 1995 Tatu Ylonen <ylo@xxxxxxxxx>, Espoo, Finland * All rights reserved * This program is the ssh daemon. It listens for connections from clients, * and performs authentication, executes use commands or shell, and forwards * information to/from the application to the user client over an encrypted * connection. This can also handle forwarding of X11, TCP/IP, and * authentication agent connections. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 implementation: * Privilege Separation: * * Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved. * Copyright (c) 2002 Niels Provos. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" RCSID("$OpenBSD: sshd.c,v 1.301 2004/08/11 11:50:09 dtucker Exp $"); #include <openssl/dh.h> #include <openssl/bn.h> #include <openssl/md5.h> #include <openssl/rand.h> #ifdef HAVE_SECUREWARE #include <sys/security.h> #include <prot.h> #endif #include "ssh.h" #include "ssh1.h" #include "ssh2.h" #include "xmalloc.h" #include "rsa.h" #include "sshpty.h" #include "packet.h" #include "log.h" #include "servconf.h" #include "uidswap.h" #include "compat.h" #include "buffer.h" #include "bufaux.h" #include "cipher.h" #include "kex.h" #include "key.h" #include "dh.h" #include "myproposal.h" #include "authfile.h" #include "pathnames.h" #include "atomicio.h" #include "canohost.h" #include "auth.h" #include "misc.h" #include "msg.h" #include "dispatch.h" #include "channels.h" #include "session.h" #include "monitor_mm.h" #include "monitor.h" #include "monitor_wrap.h" #include "monitor_fdpass.h" #ifdef LIBWRAP #include <tcpd.h> #include <syslog.h> int allow_severity = LOG_INFO; int deny_severity = LOG_WARNING; #endif /* LIBWRAP */ #ifndef O_NOCTTY #define O_NOCTTY 0 #endif /* Re-exec fds */ #define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) #define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2) #define REEXEC_CONFIG_PASS_FD (STDERR_FILENO + 3) #define REEXEC_MIN_FREE_FD (STDERR_FILENO + 4) extern char *__progname; /* Server configuration options. */ ServerOptions options; /* Name of the server configuration file. */ char *config_file_name = _PATH_SERVER_CONFIG_FILE; /* * Flag indicating whether IPv4 or IPv6. This can be set on the command line. * Default value is AF_UNSPEC means both IPv4 and IPv6. */ int IPv4or6 = AF_UNSPEC; /* * Debug mode flag. This can be set on the command line. If debug * mode is enabled, extra debugging output will be sent to the system * log, the daemon will not go to background, and will exit after processing * the first connection. */ int debug_flag = 0; /* Flag indicating that the daemon should only test the configuration and keys. */ int test_flag = 0; /* Flag indicating that the daemon is being started from inetd. */ int inetd_flag = 0; /* Flag indicating that sshd should not detach and become a daemon. */ int no_daemon_flag = 0; /* debug goes to stderr unless inetd_flag is set */ int log_stderr = 0; /* Saved arguments to main(). */ char **saved_argv; int saved_argc; /* re-exec */ int rexeced_flag = 0; int rexec_flag = 1; int rexec_argc = 0; char **rexec_argv; /* * The sockets that the server is listening; this is used in the SIGHUP * signal handler. */ #define MAX_LISTEN_SOCKS 16 int listen_socks[MAX_LISTEN_SOCKS]; int num_listen_socks = 0; /* * the client's version string, passed by sshd2 in compat mode. if != NULL, * sshd will skip the version-number exchange */ char *client_version_string = NULL; char *server_version_string = NULL; /* for rekeying XXX fixme */ Kex *xxx_kex; /* * Any really sensitive data in the application is contained in this * structure. The idea is that this structure could be locked into memory so * that the pages do not get written into swap. However, there are some * problems. The private key contains BIGNUMs, and we do not (in principle) * have access to the internals of them, and locking just the structure is * not very useful. Currently, memory locking is not implemented. */ struct { Key *server_key; /* ephemeral server key */ Key *ssh1_host_key; /* ssh1 host key */ Key **host_keys; /* all private host keys */ int have_ssh1_key; int have_ssh2_key; u_char ssh1_cookie[SSH_SESSION_KEY_LENGTH]; } sensitive_data; /* * Flag indicating whether the RSA server key needs to be regenerated. * Is set in the SIGALRM handler and cleared when the key is regenerated. */ static volatile sig_atomic_t key_do_regen = 0; /* This is set to true when a signal is received. */ static volatile sig_atomic_t received_sighup = 0; static volatile sig_atomic_t received_sigterm = 0; /* session identifier, used by RSA-auth */ u_char session_id[16]; /* same for ssh2 */ u_char *session_id2 = NULL; u_int session_id2_len = 0; /* record remote hostname or ip */ u_int utmp_len = MAXHOSTNAMELEN; /* options.max_startup sized array of fd ints */ int *startup_pipes = NULL; int startup_pipe; /* in child */ /* variables used for privilege separation */ int use_privsep; struct monitor *pmonitor = NULL; /* global authentication context */ Authctxt *the_authctxt = NULL; /* message to be displayed after login */ Buffer loginmsg; /* Prototypes for various functions defined later in this file. */ void destroy_sensitive_data(void); void demote_sensitive_data(void); static void do_ssh1_kex(void); static void do_ssh2_kex(void); /* * Close all listening sockets */ static void close_listen_socks(void) { int i; for (i = 0; i < num_listen_socks; i++) close(listen_socks[i]); num_listen_socks = -1; } static void close_startup_pipes(void) { int i; if (startup_pipes) for (i = 0; i < options.max_startups; i++) if (startup_pipes[i] != -1) close(startup_pipes[i]); } /* * Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; * the effect is to reread the configuration file (and to regenerate * the server key). */ static void sighup_handler(int sig) { int save_errno = errno; received_sighup = 1; signal(SIGHUP, sighup_handler); errno = save_errno; } /* * Called from the main program after receiving SIGHUP. * Restarts the server. */ static void sighup_restart(void) { logit("Received SIGHUP; restarting."); close_listen_socks(); close_startup_pipes(); execv(saved_argv[0], saved_argv); logit("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0], strerror(errno)); exit(1); } /* * Generic signal handler for terminating signals in the master daemon. */ static void sigterm_handler(int sig) { received_sigterm = sig; } /* * SIGCHLD handler. This is called whenever a child dies. This will then * reap any zombies left by exited children. */ static void main_sigchld_handler(int sig) { int save_errno = errno; pid_t pid; int status; while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || (pid < 0 && errno == EINTR)) ; signal(SIGCHLD, main_sigchld_handler); errno = save_errno; } /* * Signal handler for the alarm after the login grace period has expired. */ static void grace_alarm_handler(int sig) { /* XXX no idea how fix this signal handler */ if (use_privsep && pmonitor != NULL && pmonitor->m_pid > 0) kill(pmonitor->m_pid, SIGALRM); /* Log error and exit. */ fatal("Timeout before authentication for %s", get_remote_ipaddr()); } /* * Signal handler for the key regeneration alarm. Note that this * alarm only occurs in the daemon waiting for connections, and it does not * do anything with the private key or random state before forking. * Thus there should be no concurrency control/asynchronous execution * problems. */ static void generate_ephemeral_server_key(void) { u_int32_t rnd = 0; int i; verbose("Generating %s%d bit RSA key.", sensitive_data.server_key ? "new " : "", options.server_key_bits); if (sensitive_data.server_key != NULL) key_free(sensitive_data.server_key); sensitive_data.server_key = key_generate(KEY_RSA1, options.server_key_bits); verbose("RSA key generation complete."); for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) { if (i % 4 == 0) rnd = arc4random(); sensitive_data.ssh1_cookie[i] = rnd & 0xff; rnd >>= 8; } arc4random_stir(); } static void key_regeneration_alarm(int sig) { int save_errno = errno; signal(SIGALRM, SIG_DFL); errno = save_errno; key_do_regen = 1; } static void sshd_exchange_identification(int sock_in, int sock_out) { int i, mismatch; int remote_major, remote_minor; int major, minor; char *s; char buf[256]; /* Must not be larger than remote_version. */ char remote_version[256]; /* Must be at least as big as buf. */ if ((options.protocol & SSH_PROTO_1) && (options.protocol & SSH_PROTO_2)) { major = PROTOCOL_MAJOR_1; minor = 99; } else if (options.protocol & SSH_PROTO_2) { major = PROTOCOL_MAJOR_2; minor = PROTOCOL_MINOR_2; } else { major = PROTOCOL_MAJOR_1; minor = PROTOCOL_MINOR_1; } snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", major, minor, SSH_VERSION); server_version_string = xstrdup(buf); /* Send our protocol version identification. */ if (atomicio(vwrite, sock_out, server_version_string, strlen(server_version_string)) != strlen(server_version_string)) { logit("Could not write ident string to %s", get_remote_ipaddr()); cleanup_exit(255); } /* Read other sides version identification. */ memset(buf, 0, sizeof(buf)); for (i = 0; i < sizeof(buf) - 1; i++) { if (atomicio(read, sock_in, &buf[i], 1) != 1) { logit("Did not receive identification string from %s", get_remote_ipaddr()); cleanup_exit(255); } if (buf[i] == '\r') { buf[i] = 0; /* Kludge for F-Secure Macintosh < 1.0.2 */ if (i == 12 && strncmp(buf, "SSH-1.5-W1.0", 12) == 0) break; continue; } if (buf[i] == '\n') { buf[i] = 0; break; } } buf[sizeof(buf) - 1] = 0; client_version_string = xstrdup(buf); /* * Check that the versions match. In future this might accept * several versions and set appropriate flags to handle them. */ if (sscanf(client_version_string, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, remote_version) != 3) { s = "Protocol mismatch.\n"; (void) atomicio(vwrite, sock_out, s, strlen(s)); close(sock_in); close(sock_out); logit("Bad protocol version identification '%.100s' from %s", client_version_string, get_remote_ipaddr()); cleanup_exit(255); } debug("Client protocol version %d.%d; client software version %.100s", remote_major, remote_minor, remote_version); compat_datafellows(remote_version); if (datafellows & SSH_BUG_PROBE) { logit("probed from %s with %s. Don't panic.", get_remote_ipaddr(), client_version_string); cleanup_exit(255); } if (datafellows & SSH_BUG_SCANNER) { logit("scanned from %s with %s. Don't panic.", get_remote_ipaddr(), client_version_string); cleanup_exit(255); } mismatch = 0; switch (remote_major) { case 1: if (remote_minor == 99) { if (options.protocol & SSH_PROTO_2) enable_compat20(); else mismatch = 1; break; } if (!(options.protocol & SSH_PROTO_1)) { mismatch = 1; break; } if (remote_minor < 3) { packet_disconnect("Your ssh version is too old and " "is no longer supported. Please install a newer version."); } else if (remote_minor == 3) { /* note that this disables agent-forwarding */ enable_compat13(); } break; case 2: if (options.protocol & SSH_PROTO_2) { enable_compat20(); break; } /* FALLTHROUGH */ default: mismatch = 1; break; } chop(server_version_string); debug("Local version string %.200s", server_version_string); if (mismatch) { s = "Protocol major versions differ.\n"; (void) atomicio(vwrite, sock_out, s, strlen(s)); close(sock_in); close(sock_out); logit("Protocol major versions differ for %s: %.200s vs. %.200s", get_remote_ipaddr(), server_version_string, client_version_string); cleanup_exit(255); } } /* Destroy the host and server keys. They will no longer be needed. */ void destroy_sensitive_data(void) { int i; if (sensitive_data.server_key) { key_free(sensitive_data.server_key); sensitive_data.server_key = NULL; } for (i = 0; i < options.num_host_key_files; i++) { if (sensitive_data.host_keys[i]) { key_free(sensitive_data.host_keys[i]); sensitive_data.host_keys[i] = NULL; } } sensitive_data.ssh1_host_key = NULL; memset(sensitive_data.ssh1_cookie, 0, SSH_SESSION_KEY_LENGTH); } /* Demote private to public keys for network child */ void demote_sensitive_data(void) { Key *tmp; int i; if (sensitive_data.server_key) { tmp = key_demote(sensitive_data.server_key); key_free(sensitive_data.server_key); sensitive_data.server_key = tmp; } for (i = 0; i < options.num_host_key_files; i++) { if (sensitive_data.host_keys[i]) { tmp = key_demote(sensitive_data.host_keys[i]); key_free(sensitive_data.host_keys[i]); sensitive_data.host_keys[i] = tmp; if (tmp->type == KEY_RSA1) sensitive_data.ssh1_host_key = tmp; } } /* We do not clear ssh1_host key and cookie. XXX - Okay Niels? */ } static void privsep_preauth_child(void) { u_int32_t rnd[256]; gid_t gidset[1]; struct passwd *pw; int i; /* Enable challenge-response authentication for privilege separation */ privsep_challenge_enable(); for (i = 0; i < 256; i++) rnd[i] = arc4random(); RAND_seed(rnd, sizeof(rnd)); /* Demote the private keys to public keys. */ demote_sensitive_data(); if ((pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) fatal("Privilege separation user %s does not exist", SSH_PRIVSEP_USER); memset(pw->pw_passwd, 0, strlen(pw->pw_passwd)); endpwent(); #ifndef CONDOR_SSHD /* Change our root directory */ if (chroot(_PATH_PRIVSEP_CHROOT_DIR) == -1) fatal("chroot(\"%s\"): %s", _PATH_PRIVSEP_CHROOT_DIR, strerror(errno)); if (chdir("/") == -1) fatal("chdir(\"/\"): %s", strerror(errno)); #endif /* Drop our privileges */ debug3("privsep user:group %u:%u", (u_int)pw->pw_uid, (u_int)pw->pw_gid); #if 0 /* XXX not ready, too heavy after chroot */ do_setusercontext(pw); #else gidset[0] = pw->pw_gid; if (setgroups(1, gidset) < 0) fatal("setgroups: %.100s", strerror(errno)); permanently_set_uid(pw); #endif } static int privsep_preauth(Authctxt *authctxt) { int status; pid_t pid; /* Set up unprivileged child process to deal with network data */ pmonitor = monitor_init(); /* Store a pointer to the kex for later rekeying */ pmonitor->m_pkex = &xxx_kex; pid = fork(); if (pid == -1) { fatal("fork of unprivileged child failed"); } else if (pid != 0) { debug2("Network child is on pid %ld", (long)pid); close(pmonitor->m_recvfd); pmonitor->m_pid = pid; monitor_child_preauth(authctxt, pmonitor); close(pmonitor->m_sendfd); /* Sync memory */ monitor_sync(pmonitor); /* Wait for the child's exit status */ while (waitpid(pid, &status, 0) < 0) if (errno != EINTR) break; return (1); } else { /* child */ close(pmonitor->m_sendfd); /* Demote the child */ if (getuid() == 0 || geteuid() == 0) privsep_preauth_child(); setproctitle("%s", "[net]"); } return (0); } static void privsep_postauth(Authctxt *authctxt) { #ifdef DISABLE_FD_PASSING if (1) { #else if (authctxt->pw->pw_uid == 0 || options.use_login) { #endif /* File descriptor passing is broken or root login */ monitor_apply_keystate(pmonitor); use_privsep = 0; return; } /* Authentication complete */ alarm(0); if (startup_pipe != -1) { close(startup_pipe); startup_pipe = -1; } /* New socket pair */ monitor_reinit(pmonitor); pmonitor->m_pid = fork(); if (pmonitor->m_pid == -1) fatal("fork of unprivileged child failed"); else if (pmonitor->m_pid != 0) { debug2("User child is on pid %ld", (long)pmonitor->m_pid); close(pmonitor->m_recvfd); buffer_clear(&loginmsg); monitor_child_postauth(pmonitor); /* NEVERREACHED */ exit(0); } close(pmonitor->m_sendfd); /* Demote the private keys to public keys. */ demote_sensitive_data(); /* Drop privileges */ do_setusercontext(authctxt->pw); /* It is safe now to apply the key state */ monitor_apply_keystate(pmonitor); } static char * list_hostkey_types(void) { Buffer b; const char *p; char *ret; int i; buffer_init(&b); for (i = 0; i < options.num_host_key_files; i++) { Key *key = sensitive_data.host_keys[i]; if (key == NULL) continue; switch (key->type) { case KEY_RSA: case KEY_DSA: if (buffer_len(&b) > 0) buffer_append(&b, ",", 1); p = key_ssh_name(key); buffer_append(&b, p, strlen(p)); break; } } buffer_append(&b, "\0", 1); ret = xstrdup(buffer_ptr(&b)); buffer_free(&b); debug("list_hostkey_types: %s", ret); return ret; } Key * get_hostkey_by_type(int type) { int i; for (i = 0; i < options.num_host_key_files; i++) { Key *key = sensitive_data.host_keys[i]; if (key != NULL && key->type == type) return key; } return NULL; } Key * get_hostkey_by_index(int ind) { if (ind < 0 || ind >= options.num_host_key_files) return (NULL); return (sensitive_data.host_keys[ind]); } int get_hostkey_index(Key *key) { int i; for (i = 0; i < options.num_host_key_files; i++) { if (key == sensitive_data.host_keys[i]) return (i); } return (-1); } /* * returns 1 if connection should be dropped, 0 otherwise. * dropping starts at connection #max_startups_begin with a probability * of (max_startups_rate/100). the probability increases linearly until * all connections are dropped for startups > max_startups */ static int drop_connection(int startups) { double p, r; if (startups < options.max_startups_begin) return 0; if (startups >= options.max_startups) return 1; if (options.max_startups_rate == 100) return 1; p = 100 - options.max_startups_rate; p *= startups - options.max_startups_begin; p /= (double) (options.max_startups - options.max_startups_begin); p += options.max_startups_rate; p /= 100.0; r = arc4random() / (double) UINT_MAX; debug("drop_connection: p %g, r %g", p, r); return (r < p) ? 1 : 0; } static void usage(void) { fprintf(stderr, "%s, %s\n", SSH_VERSION, SSLeay_version(SSLEAY_VERSION)); fprintf(stderr, "usage: sshd [-46Ddeiqt] [-b bits] [-f config_file] [-g login_grace_time]\n" " [-h host_key_file] [-k key_gen_time] [-o option] [-p port] [-u len]\n" ); exit(1); } static void send_rexec_state(int fd, Buffer *conf) { Buffer m; debug3("%s: entering fd = %d config len %d", __func__, fd, buffer_len(conf)); /* * Protocol from reexec master to child: * string configuration * u_int ephemeral_key_follows * bignum e (only if ephemeral_key_follows == 1) * bignum n " * bignum d " * bignum iqmp " * bignum p " * bignum q " */ buffer_init(&m); buffer_put_cstring(&m, buffer_ptr(conf)); if (sensitive_data.server_key != NULL && sensitive_data.server_key->type == KEY_RSA1) { buffer_put_int(&m, 1); buffer_put_bignum(&m, sensitive_data.server_key->rsa->e); buffer_put_bignum(&m, sensitive_data.server_key->rsa->n); buffer_put_bignum(&m, sensitive_data.server_key->rsa->d); buffer_put_bignum(&m, sensitive_data.server_key->rsa->iqmp); buffer_put_bignum(&m, sensitive_data.server_key->rsa->p); buffer_put_bignum(&m, sensitive_data.server_key->rsa->q); } else buffer_put_int(&m, 0); if (ssh_msg_send(fd, 0, &m) == -1) fatal("%s: ssh_msg_send failed", __func__); buffer_free(&m); debug3("%s: done", __func__); } static void recv_rexec_state(int fd, Buffer *conf) { Buffer m; char *cp; u_int len; debug3("%s: entering fd = %d", __func__, fd); buffer_init(&m); if (ssh_msg_recv(fd, &m) == -1) fatal("%s: ssh_msg_recv failed", __func__); if (buffer_get_char(&m) != 0) fatal("%s: rexec version mismatch", __func__); cp = buffer_get_string(&m, &len); if (conf != NULL) buffer_append(conf, cp, len + 1); xfree(cp); if (buffer_get_int(&m)) { if (sensitive_data.server_key != NULL) key_free(sensitive_data.server_key); sensitive_data.server_key = key_new_private(KEY_RSA1); buffer_get_bignum(&m, sensitive_data.server_key->rsa->e); buffer_get_bignum(&m, sensitive_data.server_key->rsa->n); buffer_get_bignum(&m, sensitive_data.server_key->rsa->d); buffer_get_bignum(&m, sensitive_data.server_key->rsa->iqmp); buffer_get_bignum(&m, sensitive_data.server_key->rsa->p); buffer_get_bignum(&m, sensitive_data.server_key->rsa->q); rsa_generate_additional_parameters( sensitive_data.server_key->rsa); } buffer_free(&m); debug3("%s: done", __func__); } /* * Main program for the daemon. */ int main(int ac, char **av) { extern char *optarg; extern int optind; int opt, j, i, fdsetsz, on = 1; int sock_in = -1, sock_out = -1, newsock = -1; pid_t pid; socklen_t fromlen; fd_set *fdset; struct sockaddr_storage from; const char *remote_ip; int remote_port; FILE *f; struct addrinfo *ai; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; char *line; int listen_sock, maxfd; int startup_p[2], config_s[2]; int startups = 0; Key *key; Authctxt *authctxt; int ret, key_used = 0; Buffer cfg; #ifdef HAVE_SECUREWARE (void)set_auth_parameters(ac, av); #endif __progname = ssh_get_progname(av[0]); init_rng(); /* Save argv. Duplicate so setproctitle emulation doesn't clobber it */ saved_argc = ac; rexec_argc = ac; saved_argv = xmalloc(sizeof(*saved_argv) * (ac + 1)); for (i = 0; i < ac; i++) saved_argv[i] = xstrdup(av[i]); saved_argv[i] = NULL; #ifndef HAVE_SETPROCTITLE /* Prepare for later setproctitle emulation */ compat_init_setproctitle(ac, av); av = saved_argv; #endif if (geteuid() == 0 && setgroups(0, NULL) == -1) debug("setgroups(): %.200s", strerror(errno)); /* Initialize configuration options to their default values. */ initialize_server_options(&options); /* Parse command-line arguments. */ while ((opt = getopt(ac, av, "f:p:b:k:h:g:u:o:dDeiqrtQR46")) != -1) { switch (opt) { case '4': IPv4or6 = AF_INET; break; case '6': IPv4or6 = AF_INET6; break; case 'f': config_file_name = optarg; break; case 'd': if (debug_flag == 0) { debug_flag = 1; options.log_level = SYSLOG_LEVEL_DEBUG1; } else if (options.log_level < SYSLOG_LEVEL_DEBUG3) options.log_level++; break; case 'D': no_daemon_flag = 1; break; case 'e': log_stderr = 1; break; case 'i': inetd_flag = 1; break; case 'r': rexec_flag = 0; break; case 'R': rexeced_flag = 1; inetd_flag = 1; break; case 'Q': /* ignored */ break; case 'q': options.log_level = SYSLOG_LEVEL_QUIET; break; case 'b': options.server_key_bits = atoi(optarg); break; case 'p': options.ports_from_cmdline = 1; if (options.num_ports >= MAX_PORTS) { fprintf(stderr, "too many ports.\n"); exit(1); } options.ports[options.num_ports++] = a2port(optarg); if (options.ports[options.num_ports-1] == 0) { fprintf(stderr, "Bad port number.\n"); exit(1); } break; case 'g': if ((options.login_grace_time = convtime(optarg)) == -1) { fprintf(stderr, "Invalid login grace time.\n"); exit(1); } break; case 'k': if ((options.key_regeneration_time = convtime(optarg)) == -1) { fprintf(stderr, "Invalid key regeneration interval.\n"); exit(1); } break; case 'h': if (options.num_host_key_files >= MAX_HOSTKEYS) { fprintf(stderr, "too many host keys.\n"); exit(1); } options.host_key_files[options.num_host_key_files++] = optarg; break; case 't': test_flag = 1; break; case 'u': utmp_len = atoi(optarg); if (utmp_len > MAXHOSTNAMELEN) { fprintf(stderr, "Invalid utmp length.\n"); exit(1); } break; case 'o': line = xstrdup(optarg); if (process_server_config_line(&options, line, "command-line", 0) != 0) exit(1); xfree(line); break; case '?': default: usage(); break; } } if (rexeced_flag || inetd_flag) rexec_flag = 0; if (rexec_flag && (av[0] == NULL || *av[0] != '/')) fatal("sshd re-exec requires execution with an absolute path"); if (rexeced_flag) closefrom(REEXEC_MIN_FREE_FD); else closefrom(REEXEC_DEVCRYPTO_RESERVED_FD); SSLeay_add_all_algorithms(); channel_set_af(IPv4or6); /* * Force logging to stderr until we have loaded the private host * key (unless started from inetd) */ log_init(__progname, options.log_level == SYSLOG_LEVEL_NOT_SET ? SYSLOG_LEVEL_INFO : options.log_level, options.log_facility == SYSLOG_FACILITY_NOT_SET ? SYSLOG_FACILITY_AUTH : options.log_facility, log_stderr || !inetd_flag); #ifdef _AIX /* * Unset KRB5CCNAME, otherwise the user's session may inherit it from * root's environment */ unsetenv("KRB5CCNAME"); #endif /* _AIX */ #ifdef _UNICOS /* Cray can define user privs drop all privs now! * Not needed on PRIV_SU systems! */ drop_cray_privs(); #endif seed_rng(); sensitive_data.server_key = NULL; sensitive_data.ssh1_host_key = NULL; sensitive_data.have_ssh1_key = 0; sensitive_data.have_ssh2_key = 0; /* Fetch our configuration */ buffer_init(&cfg); if (rexeced_flag) recv_rexec_state(REEXEC_CONFIG_PASS_FD, &cfg); else load_server_config(config_file_name, &cfg); parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name, &cfg); if (!rexec_flag) buffer_free(&cfg); /* Fill in default values for those options not explicitly set. */ fill_default_server_options(&options); /* Check that there are no remaining arguments. */ if (optind < ac) { fprintf(stderr, "Extra argument %s.\n", av[optind]); exit(1); } debug("sshd version %.100s", SSH_VERSION); /* load private host keys */ sensitive_data.host_keys = xmalloc(options.num_host_key_files * sizeof(Key *)); for (i = 0; i < options.num_host_key_files; i++) sensitive_data.host_keys[i] = NULL; for (i = 0; i < options.num_host_key_files; i++) { key = key_load_private(options.host_key_files[i], "", NULL); sensitive_data.host_keys[i] = key; if (key == NULL) { error("Could not load host key: %s", options.host_key_files[i]); sensitive_data.host_keys[i] = NULL; continue; } switch (key->type) { case KEY_RSA1: sensitive_data.ssh1_host_key = key; sensitive_data.have_ssh1_key = 1; break; case KEY_RSA: case KEY_DSA: sensitive_data.have_ssh2_key = 1; break; } debug("private host key: #%d type %d %s", i, key->type, key_type(key)); } if ((options.protocol & SSH_PROTO_1) && !sensitive_data.have_ssh1_key) { logit("Disabling protocol version 1. Could not load host key"); options.protocol &= ~SSH_PROTO_1; } if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { logit("Disabling protocol version 2. Could not load host key"); options.protocol &= ~SSH_PROTO_2; } if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { logit("sshd: no hostkeys available -- exiting."); exit(1); } /* Check certain values for sanity. */ if (options.protocol & SSH_PROTO_1) { if (options.server_key_bits < 512 || options.server_key_bits > 32768) { fprintf(stderr, "Bad server key size.\n"); exit(1); } /* * Check that server and host key lengths differ sufficiently. This * is necessary to make double encryption work with rsaref. Oh, I * hate software patents. I dont know if this can go? Niels */ if (options.server_key_bits > BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) - SSH_KEY_BITS_RESERVED && options.server_key_bits < BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + SSH_KEY_BITS_RESERVED) { options.server_key_bits = BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + SSH_KEY_BITS_RESERVED; debug("Forcing server key to %d bits to make it differ from host key.", options.server_key_bits); } } if (use_privsep) { struct passwd *pw; struct stat st; if ((pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) fatal("Privilege separation user %s does not exist", SSH_PRIVSEP_USER); if ((stat(_PATH_PRIVSEP_CHROOT_DIR, &st) == -1) || (S_ISDIR(st.st_mode) == 0)) fatal("Missing privilege separation directory: %s", _PATH_PRIVSEP_CHROOT_DIR); #ifdef HAVE_CYGWIN if (check_ntsec(_PATH_PRIVSEP_CHROOT_DIR) && (st.st_uid != getuid () || (st.st_mode & (S_IWGRP|S_IWOTH)) != 0)) #else if (st.st_uid != 0 || (st.st_mode & (S_IWGRP|S_IWOTH)) != 0) #endif fatal("%s must be owned by root and not group or " "world-writable.", _PATH_PRIVSEP_CHROOT_DIR); } /* Configuration looks good, so exit if in test mode. */ if (test_flag) exit(0); /* * Clear out any supplemental groups we may have inherited. This * prevents inadvertent creation of files with bad modes (in the * portable version at least, it's certainly possible for PAM * to create a file, and we can't control the code in every * module which might be used). */ if (setgroups(0, NULL) < 0) debug("setgroups() failed: %.200s", strerror(errno)); if (rexec_flag) { rexec_argv = xmalloc(sizeof(char *) * (rexec_argc + 2)); for (i = 0; i < rexec_argc; i++) { debug("rexec_argv[%d]='%s'", i, saved_argv[i]); rexec_argv[i] = saved_argv[i]; } rexec_argv[rexec_argc] = "-R"; rexec_argv[rexec_argc + 1] = NULL; } /* Initialize the log (it is reinitialized below in case we forked). */ if (debug_flag && !inetd_flag) log_stderr = 1; log_init(__progname, options.log_level, options.log_facility, log_stderr); /* * If not in debugging mode, and not started from inetd, disconnect * from the controlling terminal, and fork. The original process * exits. */ if (!(debug_flag || inetd_flag || no_daemon_flag)) { #ifdef TIOCNOTTY int fd; #endif /* TIOCNOTTY */ if (daemon(0, 0) < 0) fatal("daemon() failed: %.200s", strerror(errno)); /* Disconnect from the controlling tty. */ #ifdef TIOCNOTTY fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); if (fd >= 0) { (void) ioctl(fd, TIOCNOTTY, NULL); close(fd); } #endif /* TIOCNOTTY */ } /* Reinitialize the log (because of the fork above). */ log_init(__progname, options.log_level, options.log_facility, log_stderr); /* Initialize the random number generator. */ arc4random_stir(); /* Chdir to the root directory so that the current disk can be unmounted if desired. */ #ifndef CONDOR_SSHD chdir("/"); #endif /* ignore SIGPIPE */ signal(SIGPIPE, SIG_IGN); /* Start listening for a socket, unless started from inetd. */ if (inetd_flag) { int fd; startup_pipe = -1; if (rexeced_flag) { close(REEXEC_CONFIG_PASS_FD); sock_in = sock_out = dup(STDIN_FILENO); if (!debug_flag) { startup_pipe = dup(REEXEC_STARTUP_PIPE_FD); close(REEXEC_STARTUP_PIPE_FD); } } else { sock_in = dup(STDIN_FILENO); sock_out = dup(STDOUT_FILENO); } /* * We intentionally do not close the descriptors 0, 1, and 2 * as our code for setting the descriptors won't work if * ttyfd happens to be one of those. */ if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); if (fd > STDOUT_FILENO) close(fd); } debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); if ((options.protocol & SSH_PROTO_1) && sensitive_data.server_key == NULL) generate_ephemeral_server_key(); } else { for (ai = options.listen_addrs; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; if (num_listen_socks >= MAX_LISTEN_SOCKS) fatal("Too many listen sockets. " "Enlarge MAX_LISTEN_SOCKS"); if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { error("getnameinfo failed"); continue; } /* Create socket for listening. */ listen_sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (listen_sock < 0) { /* kernel may not support ipv6 */ verbose("socket: %.100s", strerror(errno)); continue; } if (set_nonblock(listen_sock) == -1) { close(listen_sock); continue; } /* * Set socket options. * Allow local port reuse in TIME_WAIT. */ if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) error("setsockopt SO_REUSEADDR: %s", strerror(errno)); debug("Bind to port %s on %s.", strport, ntop); /* Bind the socket to the desired port. */ if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) { if (!ai->ai_next) error("Bind to port %s on %s failed: %.200s.", strport, ntop, strerror(errno)); close(listen_sock); continue; } listen_socks[num_listen_socks] = listen_sock; num_listen_socks++; /* Start listening on the port. */ logit("Server listening on %s port %s.", ntop, strport); if (listen(listen_sock, SSH_LISTEN_BACKLOG) < 0) fatal("listen: %.100s", strerror(errno)); } freeaddrinfo(options.listen_addrs); if (!num_listen_socks) fatal("Cannot bind any address."); if (options.protocol & SSH_PROTO_1) generate_ephemeral_server_key(); /* * Arrange to restart on SIGHUP. The handler needs * listen_sock. */ signal(SIGHUP, sighup_handler); signal(SIGTERM, sigterm_handler); signal(SIGQUIT, sigterm_handler); /* Arrange SIGCHLD to be caught. */ signal(SIGCHLD, main_sigchld_handler); /* Write out the pid file after the sigterm handler is setup */ if (!debug_flag) { /* * Record our pid in /var/run/sshd.pid to make it * easier to kill the correct sshd. We don't want to * do this before the bind above because the bind will * fail if there already is a daemon, and this will * overwrite any old pid in the file. */ f = fopen(options.pid_file, "wb"); if (f == NULL) { error("Couldn't create pid file \"%s\": %s", options.pid_file, strerror(errno)); } else { fprintf(f, "%ld\n", (long) getpid()); fclose(f); } } /* setup fd set for listen */ fdset = NULL; maxfd = 0; for (i = 0; i < num_listen_socks; i++) if (listen_socks[i] > maxfd) maxfd = listen_socks[i]; /* pipes connected to unauthenticated childs */ startup_pipes = xmalloc(options.max_startups * sizeof(int)); for (i = 0; i < options.max_startups; i++) startup_pipes[i] = -1; /* * Stay listening for connections until the system crashes or * the daemon is killed with a signal. */ for (;;) { if (received_sighup) sighup_restart(); if (fdset != NULL) xfree(fdset); fdsetsz = howmany(maxfd+1, NFDBITS) * sizeof(fd_mask); fdset = (fd_set *)xmalloc(fdsetsz); memset(fdset, 0, fdsetsz); for (i = 0; i < num_listen_socks; i++) FD_SET(listen_socks[i], fdset); for (i = 0; i < options.max_startups; i++) if (startup_pipes[i] != -1) FD_SET(startup_pipes[i], fdset); /* Wait in select until there is a connection. */ ret = select(maxfd+1, fdset, NULL, NULL, NULL); if (ret < 0 && errno != EINTR) error("select: %.100s", strerror(errno)); if (received_sigterm) { logit("Received signal %d; terminating.", (int) received_sigterm); close_listen_socks(); unlink(options.pid_file); exit(255); } if (key_used && key_do_regen) { generate_ephemeral_server_key(); key_used = 0; key_do_regen = 0; } if (ret < 0) continue; for (i = 0; i < options.max_startups; i++) if (startup_pipes[i] != -1 && FD_ISSET(startup_pipes[i], fdset)) { /* * the read end of the pipe is ready * if the child has closed the pipe * after successful authentication * or if the child has died */ close(startup_pipes[i]); startup_pipes[i] = -1; startups--; } for (i = 0; i < num_listen_socks; i++) { if (!FD_ISSET(listen_socks[i], fdset)) continue; fromlen = sizeof(from); newsock = accept(listen_socks[i], (struct sockaddr *)&from, &fromlen); if (newsock < 0) { if (errno != EINTR && errno != EWOULDBLOCK) error("accept: %.100s", strerror(errno)); continue; } if (unset_nonblock(newsock) == -1) { close(newsock); continue; } if (drop_connection(startups) == 1) { debug("drop connection #%d", startups); close(newsock); continue; } if (pipe(startup_p) == -1) { close(newsock); continue; } if (rexec_flag && socketpair(AF_UNIX, SOCK_STREAM, 0, config_s) == -1) { error("reexec socketpair: %s", strerror(errno)); close(newsock); close(startup_p[0]); close(startup_p[1]); continue; } for (j = 0; j < options.max_startups; j++) if (startup_pipes[j] == -1) { startup_pipes[j] = startup_p[0]; if (maxfd < startup_p[0]) maxfd = startup_p[0]; startups++; break; } /* * Got connection. Fork a child to handle it, unless * we are in debugging mode. */ if (debug_flag) { /* * In debugging mode. Close the listening * socket, and start processing the * connection without forking. */ debug("Server will not fork when running in debugging mode."); close_listen_socks(); sock_in = newsock; sock_out = newsock; close(startup_p[0]); close(startup_p[1]); startup_pipe = -1; pid = getpid(); if (rexec_flag) { send_rexec_state(config_s[0], &cfg); close(config_s[0]); } break; } else { /* * Normal production daemon. Fork, and have * the child process the connection. The * parent continues listening. */ if ((pid = fork()) == 0) { /* * Child. Close the listening and max_startup * sockets. Start using the accepted socket. * Reinitialize logging (since our pid has * changed). We break out of the loop to handle * the connection. */ startup_pipe = startup_p[1]; close_startup_pipes(); close_listen_socks(); sock_in = newsock; sock_out = newsock; log_init(__progname, options.log_level, options.log_facility, log_stderr); close(config_s[0]); break; } } /* Parent. Stay in the loop. */ if (pid < 0) error("fork: %.100s", strerror(errno)); else debug("Forked child %ld.", (long)pid); close(startup_p[1]); if (rexec_flag) { send_rexec_state(config_s[0], &cfg); close(config_s[0]); close(config_s[1]); } /* Mark that the key has been used (it was "given" to the child). */ if ((options.protocol & SSH_PROTO_1) && key_used == 0) { /* Schedule server key regeneration alarm. */ signal(SIGALRM, key_regeneration_alarm); alarm(options.key_regeneration_time); key_used = 1; } arc4random_stir(); /* Close the new socket (the child is now taking care of it). */ close(newsock); } /* child process check (or debug mode) */ if (num_listen_socks < 0) break; } } /* This is the child processing a new connection. */ setproctitle("%s", "[accepted]"); /* * Create a new session and process group since the 4.4BSD * setlogin() affects the entire process group. We don't * want the child to be able to affect the parent. */ #if !defined(SSHD_ACQUIRES_CTTY) /* * If setsid is called, on some platforms sshd will later acquire a * controlling terminal which will result in "could not set * controlling tty" errors. */ if (!debug_flag && !inetd_flag && setsid() < 0) error("setsid: %.100s", strerror(errno)); #endif if (rexec_flag) { int fd; debug("rexec start in %d out %d newsock %d pipe %d sock %d", sock_in, sock_out, newsock, startup_pipe, config_s[0]); dup2(newsock, STDIN_FILENO); dup2(STDIN_FILENO, STDOUT_FILENO); if (startup_pipe == -1) close(REEXEC_STARTUP_PIPE_FD); else dup2(startup_pipe, REEXEC_STARTUP_PIPE_FD); dup2(config_s[1], REEXEC_CONFIG_PASS_FD); close(config_s[1]); if (startup_pipe != -1) close(startup_pipe); execv(rexec_argv[0], rexec_argv); /* Reexec has failed, fall back and continue */ error("rexec of %s failed: %s", rexec_argv[0], strerror(errno)); recv_rexec_state(REEXEC_CONFIG_PASS_FD, NULL); log_init(__progname, options.log_level, options.log_facility, log_stderr); /* Clean up fds */ startup_pipe = REEXEC_STARTUP_PIPE_FD; close(config_s[1]); close(REEXEC_CONFIG_PASS_FD); newsock = sock_out = sock_in = dup(STDIN_FILENO); if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); if (fd > STDERR_FILENO) close(fd); } debug("rexec cleanup in %d out %d newsock %d pipe %d sock %d", sock_in, sock_out, newsock, startup_pipe, config_s[0]); } /* * Disable the key regeneration alarm. We will not regenerate the * key since we are no longer in a position to give it to anyone. We * will not restart on SIGHUP since it no longer makes sense. */ alarm(0); signal(SIGALRM, SIG_DFL); signal(SIGHUP, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGCHLD, SIG_DFL); signal(SIGINT, SIG_DFL); /* Set SO_KEEPALIVE if requested. */ if (options.tcp_keep_alive && setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0) error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); /* * Register our connection. This turns encryption off because we do * not have a key. */ packet_set_connection(sock_in, sock_out); remote_port = get_remote_port(); remote_ip = get_remote_ipaddr(); #ifdef LIBWRAP /* Check whether logins are denied from this host. */ if (packet_connection_is_on_socket()) { struct request_info req; request_init(&req, RQ_DAEMON, __progname, RQ_FILE, sock_in, 0); fromhost(&req); if (!hosts_access(&req)) { debug("Connection refused by tcp wrapper"); refuse(&req); /* NOTREACHED */ fatal("libwrap refuse returns"); } } #endif /* LIBWRAP */ /* Log the connection. */ verbose("Connection from %.500s port %d", remote_ip, remote_port); /* * We don\'t want to listen forever unless the other side * successfully authenticates itself. So we set up an alarm which is * cleared after successful authentication. A limit of zero * indicates no limit. Note that we don\'t set the alarm in debugging * mode; it is just annoying to have the server exit just when you * are about to discover the bug. */ signal(SIGALRM, grace_alarm_handler); if (!debug_flag) alarm(options.login_grace_time); sshd_exchange_identification(sock_in, sock_out); packet_set_nonblocking(); /* prepare buffers to collect authentication messages */ buffer_init(&loginmsg); /* allocate authentication context */ authctxt = xmalloc(sizeof(*authctxt)); memset(authctxt, 0, sizeof(*authctxt)); /* XXX global for cleanup, access from other modules */ the_authctxt = authctxt; if (use_privsep) if (privsep_preauth(authctxt) == 1) goto authenticated; /* prepare buffer to collect messages to display to user after login */ buffer_init(&loginmsg); /* perform the key exchange */ /* authenticate user and start session */ if (compat20) { do_ssh2_kex(); do_authentication2(authctxt); } else { do_ssh1_kex(); do_authentication(authctxt); } /* * If we use privilege separation, the unprivileged child transfers * the current keystate and exits */ if (use_privsep) { mm_send_keystate(pmonitor); exit(0); } authenticated: /* * In privilege separation, we fork another child and prepare * file descriptor passing. */ if (use_privsep) { privsep_postauth(authctxt); /* the monitor process [priv] will not return */ if (!compat20) destroy_sensitive_data(); } /* Start session. */ do_authenticated(authctxt); /* The connection has been terminated. */ verbose("Closing connection to %.100s", remote_ip); #ifdef USE_PAM if (options.use_pam) finish_pam(); #endif /* USE_PAM */ packet_close(); if (use_privsep) mm_terminate(); exit(0); } /* * Decrypt session_key_int using our private server key and private host key * (key with larger modulus first). */ int ssh1_session_key(BIGNUM *session_key_int) { int rsafail = 0; if (BN_cmp(sensitive_data.server_key->rsa->n, sensitive_data.ssh1_host_key->rsa->n) > 0) { /* Server key has bigger modulus. */ if (BN_num_bits(sensitive_data.server_key->rsa->n) < BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + SSH_KEY_BITS_RESERVED) { fatal("do_connection: %s: server_key %d < host_key %d + SSH_KEY_BITS_RESERVED %d", get_remote_ipaddr(), BN_num_bits(sensitive_data.server_key->rsa->n), BN_num_bits(sensitive_data.ssh1_host_key->rsa->n), SSH_KEY_BITS_RESERVED); } if (rsa_private_decrypt(session_key_int, session_key_int, sensitive_data.server_key->rsa) <= 0) rsafail++; if (rsa_private_decrypt(session_key_int, session_key_int, sensitive_data.ssh1_host_key->rsa) <= 0) rsafail++; } else { /* Host key has bigger modulus (or they are equal). */ if (BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) < BN_num_bits(sensitive_data.server_key->rsa->n) + SSH_KEY_BITS_RESERVED) { fatal("do_connection: %s: host_key %d < server_key %d + SSH_KEY_BITS_RESERVED %d", get_remote_ipaddr(), BN_num_bits(sensitive_data.ssh1_host_key->rsa->n), BN_num_bits(sensitive_data.server_key->rsa->n), SSH_KEY_BITS_RESERVED); } if (rsa_private_decrypt(session_key_int, session_key_int, sensitive_data.ssh1_host_key->rsa) < 0) rsafail++; if (rsa_private_decrypt(session_key_int, session_key_int, sensitive_data.server_key->rsa) < 0) rsafail++; } return (rsafail); } /* * SSH1 key exchange */ static void do_ssh1_kex(void) { int i, len; int rsafail = 0; BIGNUM *session_key_int; u_char session_key[SSH_SESSION_KEY_LENGTH]; u_char cookie[8]; u_int cipher_type, auth_mask, protocol_flags; u_int32_t rnd = 0; /* * Generate check bytes that the client must send back in the user * packet in order for it to be accepted; this is used to defy ip * spoofing attacks. Note that this only works against somebody * doing IP spoofing from a remote machine; any machine on the local * network can still see outgoing packets and catch the random * cookie. This only affects rhosts authentication, and this is one * of the reasons why it is inherently insecure. */ for (i = 0; i < 8; i++) { if (i % 4 == 0) rnd = arc4random(); cookie[i] = rnd & 0xff; rnd >>= 8; } /* * Send our public key. We include in the packet 64 bits of random * data that must be matched in the reply in order to prevent IP * spoofing. */ packet_start(SSH_SMSG_PUBLIC_KEY); for (i = 0; i < 8; i++) packet_put_char(cookie[i]); /* Store our public server RSA key. */ packet_put_int(BN_num_bits(sensitive_data.server_key->rsa->n)); packet_put_bignum(sensitive_data.server_key->rsa->e); packet_put_bignum(sensitive_data.server_key->rsa->n); /* Store our public host RSA key. */ packet_put_int(BN_num_bits(sensitive_data.ssh1_host_key->rsa->n)); packet_put_bignum(sensitive_data.ssh1_host_key->rsa->e); packet_put_bignum(sensitive_data.ssh1_host_key->rsa->n); /* Put protocol flags. */ packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); /* Declare which ciphers we support. */ packet_put_int(cipher_mask_ssh1(0)); /* Declare supported authentication types. */ auth_mask = 0; if (options.rhosts_rsa_authentication) auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA; if (options.rsa_authentication) auth_mask |= 1 << SSH_AUTH_RSA; if (options.challenge_response_authentication == 1) auth_mask |= 1 << SSH_AUTH_TIS; if (options.password_authentication) auth_mask |= 1 << SSH_AUTH_PASSWORD; packet_put_int(auth_mask); /* Send the packet and wait for it to be sent. */ packet_send(); packet_write_wait(); debug("Sent %d bit server key and %d bit host key.", BN_num_bits(sensitive_data.server_key->rsa->n), BN_num_bits(sensitive_data.ssh1_host_key->rsa->n)); /* Read clients reply (cipher type and session key). */ packet_read_expect(SSH_CMSG_SESSION_KEY); /* Get cipher type and check whether we accept this. */ cipher_type = packet_get_char(); if (!(cipher_mask_ssh1(0) & (1 << cipher_type))) packet_disconnect("Warning: client selects unsupported cipher."); /* Get check bytes from the packet. These must match those we sent earlier with the public key packet. */ for (i = 0; i < 8; i++) if (cookie[i] != packet_get_char()) packet_disconnect("IP Spoofing check bytes do not match."); debug("Encryption type: %.200s", cipher_name(cipher_type)); /* Get the encrypted integer. */ if ((session_key_int = BN_new()) == NULL) fatal("do_ssh1_kex: BN_new failed"); packet_get_bignum(session_key_int); protocol_flags = packet_get_int(); packet_set_protocol_flags(protocol_flags); packet_check_eom(); /* Decrypt session_key_int using host/server keys */ rsafail = PRIVSEP(ssh1_session_key(session_key_int)); /* * Extract session key from the decrypted integer. The key is in the * least significant 256 bits of the integer; the first byte of the * key is in the highest bits. */ if (!rsafail) { BN_mask_bits(session_key_int, sizeof(session_key) * 8); len = BN_num_bytes(session_key_int); if (len < 0 || len > sizeof(session_key)) { error("do_connection: bad session key len from %s: " "session_key_int %d > sizeof(session_key) %lu", get_remote_ipaddr(), len, (u_long)sizeof(session_key)); rsafail++; } else { memset(session_key, 0, sizeof(session_key)); BN_bn2bin(session_key_int, session_key + sizeof(session_key) - len); derive_ssh1_session_id( sensitive_data.ssh1_host_key->rsa->n, sensitive_data.server_key->rsa->n, cookie, session_id); /* * Xor the first 16 bytes of the session key with the * session id. */ for (i = 0; i < 16; i++) session_key[i] ^= session_id[i]; } } if (rsafail) { int bytes = BN_num_bytes(session_key_int); u_char *buf = xmalloc(bytes); MD5_CTX md; logit("do_connection: generating a fake encryption key"); BN_bn2bin(session_key_int, buf); MD5_Init(&md); MD5_Update(&md, buf, bytes); MD5_Update(&md, sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH); MD5_Final(session_key, &md); MD5_Init(&md); MD5_Update(&md, session_key, 16); MD5_Update(&md, buf, bytes); MD5_Update(&md, sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH); MD5_Final(session_key + 16, &md); memset(buf, 0, bytes); xfree(buf); for (i = 0; i < 16; i++) session_id[i] = session_key[i] ^ session_key[i + 16]; } /* Destroy the private and public keys. No longer. */ destroy_sensitive_data(); if (use_privsep) mm_ssh1_session_id(session_id); /* Destroy the decrypted integer. It is no longer needed. */ BN_clear_free(session_key_int); /* Set the session key. From this on all communications will be encrypted. */ packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type); /* Destroy our copy of the session key. It is no longer needed. */ memset(session_key, 0, sizeof(session_key)); debug("Received session key; encryption turned on."); /* Send an acknowledgment packet. Note that this packet is sent encrypted. */ packet_start(SSH_SMSG_SUCCESS); packet_send(); packet_write_wait(); } /* * SSH2 key exchange: diffie-hellman-group1-sha1 */ static void do_ssh2_kex(void) { Kex *kex; if (options.ciphers != NULL) { myproposal[PROPOSAL_ENC_ALGS_CTOS] = myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; } myproposal[PROPOSAL_ENC_ALGS_CTOS] = compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]); myproposal[PROPOSAL_ENC_ALGS_STOC] = compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]); if (options.macs != NULL) { myproposal[PROPOSAL_MAC_ALGS_CTOS] = myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; } if (!options.compression) { myproposal[PROPOSAL_COMP_ALGS_CTOS] = myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; } myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types(); /* start key exchange */ kex = kex_setup(myproposal); kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; kex->server = 1; kex->client_version_string=client_version_string; kex->server_version_string=server_version_string; kex->load_host_key=&get_hostkey_by_type; kex->host_key_index=&get_hostkey_index; xxx_kex = kex; dispatch_run(DISPATCH_BLOCK, &kex->done, kex); session_id2 = kex->session_id; session_id2_len = kex->session_id_len; #ifdef DEBUG_KEXDH /* send 1st encrypted/maced/compressed message */ packet_start(SSH2_MSG_IGNORE); packet_put_cstring("markus"); packet_send(); packet_write_wait(); #endif debug("KEX done"); } /* server specific fatal cleanup */ void cleanup_exit(int i) { if (the_authctxt) do_cleanup(the_authctxt); _exit(i); }
Attachment:
condor_sshd
Description: Binary data