123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856 |
- /* $OpenBSD: session.c,v 1.324 2020/07/07 02:47:21 deraadt Exp $ */
- /*
- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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"
- #include <sys/types.h>
- #include <sys/param.h>
- #ifdef HAVE_SYS_STAT_H
- # include <sys/stat.h>
- #endif
- #include <sys/socket.h>
- #include <sys/un.h>
- #include <sys/wait.h>
- #include <arpa/inet.h>
- #include <ctype.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <grp.h>
- #include <netdb.h>
- #ifdef HAVE_PATHS_H
- #include <paths.h>
- #endif
- #include <pwd.h>
- #include <signal.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <stdarg.h>
- #include <unistd.h>
- #include <limits.h>
- #include "openbsd-compat/sys-queue.h"
- #include "xmalloc.h"
- #include "ssh.h"
- #include "ssh2.h"
- #include "sshpty.h"
- #include "packet.h"
- #include "sshbuf.h"
- #include "ssherr.h"
- #include "match.h"
- #include "uidswap.h"
- #include "compat.h"
- #include "channels.h"
- #include "sshkey.h"
- #include "cipher.h"
- #ifdef GSSAPI
- #include "ssh-gss.h"
- #endif
- #include "hostfile.h"
- #include "auth.h"
- #include "auth-options.h"
- #include "authfd.h"
- #include "pathnames.h"
- #include "log.h"
- #include "misc.h"
- #include "servconf.h"
- #include "sshlogin.h"
- #include "serverloop.h"
- #include "canohost.h"
- #include "session.h"
- #include "kex.h"
- #include "monitor_wrap.h"
- #include "sftp.h"
- #include "atomicio.h"
- #if defined(KRB5) && defined(USE_AFS)
- #include <kafs.h>
- #endif
- #ifdef WITH_SELINUX
- #include <selinux/selinux.h>
- #endif
- #define IS_INTERNAL_SFTP(c) \
- (!strncmp(c, INTERNAL_SFTP_NAME, sizeof(INTERNAL_SFTP_NAME) - 1) && \
- (c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\0' || \
- c[sizeof(INTERNAL_SFTP_NAME) - 1] == ' ' || \
- c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\t'))
- /* func */
- Session *session_new(void);
- void session_set_fds(struct ssh *, Session *, int, int, int, int, int);
- void session_pty_cleanup(Session *);
- void session_proctitle(Session *);
- int session_setup_x11fwd(struct ssh *, Session *);
- int do_exec_pty(struct ssh *, Session *, const char *);
- int do_exec_no_pty(struct ssh *, Session *, const char *);
- int do_exec(struct ssh *, Session *, const char *);
- void do_login(struct ssh *, Session *, const char *);
- void do_child(struct ssh *, Session *, const char *);
- void do_motd(void);
- int check_quietlogin(Session *, const char *);
- static void do_authenticated2(struct ssh *, Authctxt *);
- static int session_pty_req(struct ssh *, Session *);
- /* import */
- extern ServerOptions options;
- extern char *__progname;
- extern int debug_flag;
- extern u_int utmp_len;
- extern int startup_pipe;
- extern void destroy_sensitive_data(struct ssh *, int);
- extern struct sshbuf *loginmsg;
- extern struct sshauthopt *auth_opts;
- extern char *tun_fwd_ifnames; /* serverloop.c */
- /* original command from peer. */
- const char *original_command = NULL;
- /* data */
- static int sessions_first_unused = -1;
- static int sessions_nalloc = 0;
- static Session *sessions = NULL;
- #define SUBSYSTEM_NONE 0
- #define SUBSYSTEM_EXT 1
- #define SUBSYSTEM_INT_SFTP 2
- #define SUBSYSTEM_INT_SFTP_ERROR 3
- #ifdef HAVE_LOGIN_CAP
- login_cap_t *lc;
- #endif
- static int is_child = 0;
- static int in_chroot = 0;
- static int have_dev_log = 1;
- /* File containing userauth info, if ExposeAuthInfo set */
- static char *auth_info_file = NULL;
- /* 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 ssh *ssh, struct passwd * pw)
- {
- Channel *nc;
- int sock = -1;
- 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_dir = xstrdup("/tmp/ssh-XXXXXXXXXX");
- /* Create private directory for socket */
- if (mkdtemp(auth_sock_dir) == NULL) {
- ssh_packet_send_debug(ssh, "Agent forwarding disabled: "
- "mkdtemp() failed: %.100s", strerror(errno));
- restore_uid();
- free(auth_sock_dir);
- auth_sock_dir = NULL;
- goto authsock_err;
- }
- xasprintf(&auth_sock_name, "%s/agent.%ld",
- auth_sock_dir, (long) getpid());
- /* Start a Unix listener on auth_sock_name. */
- sock = unix_listener(auth_sock_name, SSH_LISTEN_BACKLOG, 0);
- /* Restore the privileged uid. */
- restore_uid();
- /* Check for socket/bind/listen failure. */
- if (sock < 0)
- goto authsock_err;
- /* Allocate a channel for the authentication agent socket. */
- /* this shouldn't matter if its hpn or not - cjr */
- nc = channel_new(ssh, "auth socket",
- SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1,
- CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
- 0, "auth socket", 1);
- nc->path = xstrdup(auth_sock_name);
- return 1;
- authsock_err:
- free(auth_sock_name);
- if (auth_sock_dir != NULL) {
- temporarily_use_uid(pw);
- rmdir(auth_sock_dir);
- restore_uid();
- free(auth_sock_dir);
- }
- if (sock != -1)
- close(sock);
- auth_sock_name = NULL;
- auth_sock_dir = NULL;
- return 0;
- }
- static void
- display_loginmsg(void)
- {
- int r;
- if (sshbuf_len(loginmsg) == 0)
- return;
- if ((r = sshbuf_put_u8(loginmsg, 0)) != 0)
- fatal("%s: buffer error: %s", __func__, ssh_err(r));
- printf("%s", (char *)sshbuf_ptr(loginmsg));
- sshbuf_reset(loginmsg);
- }
- static void
- prepare_auth_info_file(struct passwd *pw, struct sshbuf *info)
- {
- int fd = -1, success = 0;
- if (!options.expose_userauth_info || info == NULL)
- return;
- temporarily_use_uid(pw);
- auth_info_file = xstrdup("/tmp/sshauth.XXXXXXXXXXXXXXX");
- if ((fd = mkstemp(auth_info_file)) == -1) {
- error("%s: mkstemp: %s", __func__, strerror(errno));
- goto out;
- }
- if (atomicio(vwrite, fd, sshbuf_mutable_ptr(info),
- sshbuf_len(info)) != sshbuf_len(info)) {
- error("%s: write: %s", __func__, strerror(errno));
- goto out;
- }
- if (close(fd) != 0) {
- error("%s: close: %s", __func__, strerror(errno));
- goto out;
- }
- success = 1;
- out:
- if (!success) {
- if (fd != -1)
- close(fd);
- free(auth_info_file);
- auth_info_file = NULL;
- }
- restore_uid();
- }
- static void
- set_fwdpermit_from_authopts(struct ssh *ssh, const struct sshauthopt *opts)
- {
- char *tmp, *cp, *host;
- int port;
- size_t i;
- if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0) {
- channel_clear_permission(ssh, FORWARD_USER, FORWARD_LOCAL);
- for (i = 0; i < auth_opts->npermitopen; i++) {
- tmp = cp = xstrdup(auth_opts->permitopen[i]);
- /* This shouldn't fail as it has already been checked */
- if ((host = hpdelim(&cp)) == NULL)
- fatal("%s: internal error: hpdelim", __func__);
- host = cleanhostname(host);
- if (cp == NULL || (port = permitopen_port(cp)) < 0)
- fatal("%s: internal error: permitopen port",
- __func__);
- channel_add_permission(ssh,
- FORWARD_USER, FORWARD_LOCAL, host, port);
- free(tmp);
- }
- }
- if ((options.allow_tcp_forwarding & FORWARD_REMOTE) != 0) {
- channel_clear_permission(ssh, FORWARD_USER, FORWARD_REMOTE);
- for (i = 0; i < auth_opts->npermitlisten; i++) {
- tmp = cp = xstrdup(auth_opts->permitlisten[i]);
- /* This shouldn't fail as it has already been checked */
- if ((host = hpdelim(&cp)) == NULL)
- fatal("%s: internal error: hpdelim", __func__);
- host = cleanhostname(host);
- if (cp == NULL || (port = permitopen_port(cp)) < 0)
- fatal("%s: internal error: permitlisten port",
- __func__);
- channel_add_permission(ssh,
- FORWARD_USER, FORWARD_REMOTE, host, port);
- free(tmp);
- }
- }
- }
- void
- do_authenticated(struct ssh *ssh, Authctxt *authctxt)
- {
- setproctitle("%s", authctxt->pw->pw_name);
- auth_log_authopts("active", auth_opts, 0);
- /* setup the channel layer */
- /* XXX - streamlocal? */
- set_fwdpermit_from_authopts(ssh, auth_opts);
- if (!auth_opts->permit_port_forwarding_flag ||
- options.disable_forwarding) {
- channel_disable_admin(ssh, FORWARD_LOCAL);
- channel_disable_admin(ssh, FORWARD_REMOTE);
- } else {
- if ((options.allow_tcp_forwarding & FORWARD_LOCAL) == 0)
- channel_disable_admin(ssh, FORWARD_LOCAL);
- else
- channel_permit_all(ssh, FORWARD_LOCAL);
- if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0)
- channel_disable_admin(ssh, FORWARD_REMOTE);
- else
- channel_permit_all(ssh, FORWARD_REMOTE);
- }
- auth_debug_send(ssh);
- prepare_auth_info_file(authctxt->pw, authctxt->session_info);
- do_authenticated2(ssh, authctxt);
- do_cleanup(ssh, authctxt);
- }
- /* Check untrusted xauth strings for metacharacters */
- static int
- xauth_valid_string(const char *s)
- {
- size_t i;
- for (i = 0; s[i] != '\0'; i++) {
- if (!isalnum((u_char)s[i]) &&
- s[i] != '.' && s[i] != ':' && s[i] != '/' &&
- s[i] != '-' && s[i] != '_')
- return 0;
- }
- return 1;
- }
- #define USE_PIPES 1
- /*
- * 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.
- */
- int
- do_exec_no_pty(struct ssh *ssh, Session *s, const char *command)
- {
- pid_t pid;
- #ifdef USE_PIPES
- int pin[2], pout[2], perr[2];
- if (s == NULL)
- fatal("do_exec_no_pty: no session");
- /* Allocate pipes for communicating with the program. */
- if (pipe(pin) == -1) {
- error("%s: pipe in: %.100s", __func__, strerror(errno));
- return -1;
- }
- if (pipe(pout) == -1) {
- error("%s: pipe out: %.100s", __func__, strerror(errno));
- close(pin[0]);
- close(pin[1]);
- return -1;
- }
- if (pipe(perr) == -1) {
- error("%s: pipe err: %.100s", __func__,
- strerror(errno));
- close(pin[0]);
- close(pin[1]);
- close(pout[0]);
- close(pout[1]);
- return -1;
- }
- #else
- int inout[2], err[2];
- if (s == NULL)
- fatal("do_exec_no_pty: no session");
- /* Uses socket pairs to communicate with the program. */
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) {
- error("%s: socketpair #1: %.100s", __func__, strerror(errno));
- return -1;
- }
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) == -1) {
- error("%s: socketpair #2: %.100s", __func__,
- strerror(errno));
- close(inout[0]);
- close(inout[1]);
- return -1;
- }
- #endif
- session_proctitle(s);
- /* Fork the child. */
- switch ((pid = fork())) {
- case -1:
- error("%s: fork: %.100s", __func__, strerror(errno));
- #ifdef USE_PIPES
- close(pin[0]);
- close(pin[1]);
- close(pout[0]);
- close(pout[1]);
- close(perr[0]);
- close(perr[1]);
- #else
- close(inout[0]);
- close(inout[1]);
- close(err[0]);
- close(err[1]);
- #endif
- return -1;
- case 0:
- is_child = 1;
-
- /*
- * Create a new session and process group since the 4.4BSD
- * setlogin() affects the entire process group.
- */
- if (setsid() == -1)
- 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) == -1)
- perror("dup2 stdin");
- close(pin[0]);
- /* Redirect stdout. */
- close(pout[0]);
- if (dup2(pout[1], 1) == -1)
- perror("dup2 stdout");
- close(pout[1]);
- /* Redirect stderr. */
- close(perr[0]);
- if (dup2(perr[1], 2) == -1)
- perror("dup2 stderr");
- close(perr[1]);
- #else
- /*
- * 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) == -1) /* stdin */
- perror("dup2 stdin");
- if (dup2(inout[0], 1) == -1) /* stdout (same as stdin) */
- perror("dup2 stdout");
- close(inout[0]);
- if (dup2(err[0], 2) == -1) /* stderr */
- perror("dup2 stderr");
- close(err[0]);
- #endif
- /* Do processing for the child (exec command etc). */
- do_child(ssh, s, command);
- /* NOTREACHED */
- default:
- break;
- }
- #ifdef HAVE_CYGWIN
- cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
- #endif
- s->pid = pid;
- /* Set interactive/non-interactive mode. */
- ssh_packet_set_interactive(ssh, s->display != NULL,
- options.ip_qos_interactive, options.ip_qos_bulk);
- /*
- * 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.
- */
- sshbuf_reset(loginmsg);
- #ifdef USE_PIPES
- /* We are the parent. Close the child sides of the pipes. */
- close(pin[0]);
- close(pout[1]);
- close(perr[1]);
- session_set_fds(ssh, s, pin[1], pout[0], perr[0],
- s->is_subsystem, 0);
- #else
- /* We are the parent. Close the child sides of the socket pairs. */
- close(inout[0]);
- close(err[0]);
- /*
- * Enter the interactive session. Note: server_loop must be able to
- * handle the case that fdin and fdout are the same.
- */
- session_set_fds(ssh, s, inout[1], inout[1], err[1],
- s->is_subsystem, 0);
- #endif
- return 0;
- }
- /*
- * 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.
- */
- int
- do_exec_pty(struct ssh *ssh, 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;
- /*
- * 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.
- * Do this before forking (and cleanup in the child) so as to
- * detect and gracefully fail out-of-fd conditions.
- */
- if ((fdout = dup(ptyfd)) == -1) {
- error("%s: dup #1: %s", __func__, strerror(errno));
- close(ttyfd);
- close(ptyfd);
- return -1;
- }
- /* we keep a reference to the pty master */
- if ((ptymaster = dup(ptyfd)) == -1) {
- error("%s: dup #2: %s", __func__, strerror(errno));
- close(ttyfd);
- close(ptyfd);
- close(fdout);
- return -1;
- }
- /* Fork the child. */
- switch ((pid = fork())) {
- case -1:
- error("%s: fork: %.100s", __func__, strerror(errno));
- close(fdout);
- close(ptymaster);
- close(ttyfd);
- close(ptyfd);
- return -1;
- case 0:
- is_child = 1;
-
- close(fdout);
- close(ptymaster);
- /* 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) == -1)
- error("dup2 stdin: %s", strerror(errno));
- if (dup2(ttyfd, 1) == -1)
- error("dup2 stdout: %s", strerror(errno));
- if (dup2(ttyfd, 2) == -1)
- 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
- do_login(ssh, s, command);
- #endif
- /*
- * Do common processing for the child, such as execing
- * the command.
- */
- do_child(ssh, s, command);
- /* NOTREACHED */
- default:
- break;
- }
- #ifdef HAVE_CYGWIN
- cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
- #endif
- s->pid = pid;
- /* Parent. Close the slave side of the pseudo tty. */
- close(ttyfd);
- #if !defined(HAVE_OSF_SIA) && defined(SSH_AUDIT_EVENTS)
- /* do_login in the child did not affect state in this process,
- compensate. From an architectural standpoint, this is extremely
- ugly. */
- if (command != NULL)
- audit_count_session_open();
- #endif
- /* Enter interactive session. */
- s->ptymaster = ptymaster;
- ssh_packet_set_interactive(ssh, 1,
- options.ip_qos_interactive, options.ip_qos_bulk);
- session_set_fds(ssh, s, ptyfd, fdout, -1, 1, 1);
- return 0;
- }
- /*
- * This is called to fork and execute a command. If another command is
- * to be forced, execute that instead.
- */
- int
- do_exec(struct ssh *ssh, Session *s, const char *command)
- {
- int ret;
- const char *forced = NULL, *tty = NULL;
- char session_type[1024];
- struct stat dev_log_stat;
- if (options.adm_forced_command) {
- original_command = command;
- command = options.adm_forced_command;
- forced = "(config)";
- } else if (auth_opts->force_command != NULL) {
- original_command = command;
- command = auth_opts->force_command;
- forced = "(key-option)";
- }
- #ifdef GSSAPI
- #ifdef KRB5 /* k5users_allowed_cmds only available w/ GSSAPI+KRB5 */
- else if (k5users_allowed_cmds) {
- const char *match = command;
- int allowed = 0, i = 0;
- if (!match)
- match = s->pw->pw_shell;
- while (k5users_allowed_cmds[i]) {
- if (strcmp(match, k5users_allowed_cmds[i++]) == 0) {
- debug("Allowed command '%.900s'", match);
- allowed = 1;
- break;
- }
- }
- if (!allowed) {
- debug("command '%.900s' not allowed", match);
- return 1;
- }
- }
- #endif
- #endif
- s->forced = 0;
- if (forced != NULL) {
- s->forced = 1;
- if (IS_INTERNAL_SFTP(command)) {
- s->is_subsystem = s->is_subsystem ?
- SUBSYSTEM_INT_SFTP : SUBSYSTEM_INT_SFTP_ERROR;
- } else if (s->is_subsystem)
- s->is_subsystem = SUBSYSTEM_EXT;
- snprintf(session_type, sizeof(session_type),
- "forced-command %s '%.900s'", forced, command);
- } else if (s->is_subsystem) {
- snprintf(session_type, sizeof(session_type),
- "subsystem '%.900s'", s->subsys);
- } else if (command == NULL) {
- snprintf(session_type, sizeof(session_type), "shell");
- } else {
- /* NB. we don't log unforced commands to preserve privacy */
- snprintf(session_type, sizeof(session_type), "command");
- }
- if (s->ttyfd != -1) {
- tty = s->tty;
- if (strncmp(tty, "/dev/", 5) == 0)
- tty += 5;
- }
- if (lstat("/dev/log", &dev_log_stat) != 0) {
- have_dev_log = 0;
- }
- verbose("Starting session: %s%s%s for %s from %.200s port %d id %d",
- session_type,
- tty == NULL ? "" : " on ",
- tty == NULL ? "" : tty,
- s->pw->pw_name,
- ssh_remote_ipaddr(ssh),
- ssh_remote_port(ssh),
- s->self);
- #ifdef SSH_AUDIT_EVENTS
- if (s->command != NULL || s->command_handle != -1)
- fatal("do_exec: command already set");
- if (command != NULL)
- s->command = xstrdup(command);
- else if (s->ttyfd == -1) {
- char *shell = s->pw->pw_shell;
- if (shell[0] == '\0') /* empty shell means /bin/sh */
- shell =_PATH_BSHELL;
- s->command = xstrdup(shell);
- }
- if (s->command != NULL && s->ptyfd == -1)
- s->command_handle = PRIVSEP(audit_run_command(ssh, s->command));
- #endif
- if (s->ttyfd != -1)
- ret = do_exec_pty(ssh, s, command);
- else
- ret = do_exec_no_pty(ssh, 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.
- */
- sshbuf_reset(loginmsg);
- return ret;
- }
- /* administrative, login(1)-like work */
- void
- do_login(struct ssh *ssh, 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 (ssh_packet_connection_is_on_socket(ssh)) {
- if (getpeername(ssh_packet_get_connection_in(ssh),
- (struct sockaddr *)&from, &fromlen) == -1) {
- 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,
- session_get_remote_name_or_ip(ssh, 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;
- }
- /*
- * 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.
- * If allowlist is not NULL, then it is interpreted as a pattern list and
- * only variable names that match it will be accepted.
- */
- static void
- read_environment_file(char ***env, u_int *envsize,
- const char *filename, const char *allowlist)
- {
- FILE *f;
- char *line = NULL, *cp, *value;
- size_t linesize = 0;
- u_int lineno = 0;
- f = fopen(filename, "r");
- if (!f)
- return;
- while (getline(&line, &linesize, f) != -1) {
- if (++lineno > 1000)
- fatal("Too many lines in environment file %s", filename);
- for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
- ;
- if (!*cp || *cp == '#' || *cp == '\n')
- continue;
- cp[strcspn(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++;
- if (allowlist != NULL &&
- match_pattern_list(cp, allowlist, 0) != 1)
- continue;
- child_set_env(env, envsize, cp, value);
- }
- free(line);
- 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",
- options.permit_user_env_allowlist);
- 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++)
- free(tmpenv[i]);
- free(tmpenv);
- }
- #endif /* HAVE_ETC_DEFAULT_LOGIN */
- #if defined(USE_PAM) || defined(HAVE_CYGWIN)
- static void
- copy_environment_denylist(char **source, char ***env, u_int *envsize,
- const char *denylist)
- {
- 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) {
- free(var_name);
- continue;
- }
- *var_val++ = '\0';
- if (denylist == NULL ||
- match_pattern_list(var_name, denylist, 0) != 1) {
- debug3("Copy environment: %s=%s", var_name, var_val);
- child_set_env(env, envsize, var_name, var_val);
- }
- free(var_name);
- }
- }
- #endif /* defined(USE_PAM) || defined(HAVE_CYGWIN) */
- #ifdef HAVE_CYGWIN
- static void
- copy_environment(char **source, char ***env, u_int *envsize)
- {
- copy_environment_denylist(source, env, envsize, NULL);
- }
- #endif
- static char **
- do_setup_env(struct ssh *ssh, Session *s, const char *shell)
- {
- char buf[256];
- size_t n;
- u_int i, envsize;
- char *ocp, *cp, *value, **env, *laddr;
- struct passwd *pw = s->pw;
- #if !defined (HAVE_LOGIN_CAP) && !defined (HAVE_CYGWIN)
- char *path = NULL;
- #endif
- /* Initialize the environment. */
- envsize = 100;
- env = xcalloc(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.
- */
- {
- char **p;
- p = fetch_windows_environment();
- copy_environment(p, &env, &envsize);
- free_windows_environment(p);
- }
- #endif
- #ifdef GSSAPI
- /* Allow any GSSAPI methods that we've used to alter
- * the child's environment as they see fit
- */
- ssh_gssapi_do_child(&env, &envsize);
- #endif
- /* 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 */
- if (!options.use_pam) {
- snprintf(buf, sizeof buf, "%.200s/%.50s",
- _PATH_MAILDIR, pw->pw_name);
- child_set_env(&env, &envsize, "MAIL", buf);
- }
- /* Normal systems set SHELL by default. */
- child_set_env(&env, &envsize, "SHELL", shell);
- if (getenv("TZ"))
- child_set_env(&env, &envsize, "TZ", getenv("TZ"));
- if (s->term)
- child_set_env(&env, &envsize, "TERM", s->term);
- if (s->display)
- child_set_env(&env, &envsize, "DISPLAY", s->display);
- /*
- * Since we clear KRB5CCNAME at startup, if it's set now then it
- * must have been set by a native authentication method (eg AIX or
- * SIA), so copy it to the child.
- */
- {
- char *cp;
- if ((cp = getenv("KRB5CCNAME")) != NULL)
- child_set_env(&env, &envsize, "KRB5CCNAME", cp);
- }
- #ifdef _AIX
- {
- char *cp;
- if ((cp = getenv("AUTHSTATE")) != NULL)
- child_set_env(&env, &envsize, "AUTHSTATE", cp);
- read_environment_file(&env, &envsize, "/etc/environment",
- options.permit_user_env_allowlist);
- }
- #endif
- #ifdef KRB5
- if (s->authctxt->krb5_ccname)
- child_set_env(&env, &envsize, "KRB5CCNAME",
- s->authctxt->krb5_ccname);
- #endif
- if (auth_sock_name != NULL)
- child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME,
- auth_sock_name);
- /* Set custom environment options from pubkey authentication. */
- if (options.permit_user_env) {
- for (n = 0 ; n < auth_opts->nenv; n++) {
- ocp = xstrdup(auth_opts->env[n]);
- cp = strchr(ocp, '=');
- if (cp != NULL) {
- *cp = '\0';
- /* Apply PermitUserEnvironment allowlist */
- if (options.permit_user_env_allowlist == NULL ||
- match_pattern_list(ocp,
- options.permit_user_env_allowlist, 0) == 1)
- child_set_env(&env, &envsize,
- ocp, cp + 1);
- }
- free(ocp);
- }
- }
- /* read $HOME/.ssh/environment. */
- if (options.permit_user_env) {
- snprintf(buf, sizeof buf, "%.200s/%s/environment",
- pw->pw_dir, _PATH_SSH_USER_DIR);
- read_environment_file(&env, &envsize, buf,
- options.permit_user_env_allowlist);
- }
- #ifdef USE_PAM
- /*
- * Pull in any environment variables that may have
- * been set by PAM.
- */
- if (options.use_pam) {
- char **p;
- /*
- * Don't allow PAM-internal env vars to leak
- * back into the session environment.
- */
- #define PAM_ENV_DENYLIST "SSH_AUTH_INFO*,SSH_CONNECTION*"
- p = fetch_pam_child_environment();
- copy_environment_denylist(p, &env, &envsize,
- PAM_ENV_DENYLIST);
- free_pam_environment(p);
- p = fetch_pam_environment();
- copy_environment_denylist(p, &env, &envsize,
- PAM_ENV_DENYLIST);
- free_pam_environment(p);
- }
- #endif /* USE_PAM */
- /* Environment specified by admin */
- for (i = 0; i < options.num_setenv; i++) {
- cp = xstrdup(options.setenv[i]);
- if ((value = strchr(cp, '=')) == NULL) {
- /* shouldn't happen; vars are checked in servconf.c */
- fatal("Invalid config SetEnv: %s", options.setenv[i]);
- }
- *value++ = '\0';
- child_set_env(&env, &envsize, cp, value);
- }
- /* SSH_CLIENT deprecated */
- snprintf(buf, sizeof buf, "%.50s %d %d",
- ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
- ssh_local_port(ssh));
- child_set_env(&env, &envsize, "SSH_CLIENT", buf);
- laddr = get_local_ipaddr(ssh_packet_get_connection_in(ssh));
- snprintf(buf, sizeof buf, "%.50s %d %.50s %d",
- ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
- laddr, ssh_local_port(ssh));
- free(laddr);
- child_set_env(&env, &envsize, "SSH_CONNECTION", buf);
- if (tun_fwd_ifnames != NULL)
- child_set_env(&env, &envsize, "SSH_TUNNEL", tun_fwd_ifnames);
- if (auth_info_file != NULL)
- child_set_env(&env, &envsize, "SSH_USER_AUTH", auth_info_file);
- if (s->ttyfd != -1)
- child_set_env(&env, &envsize, "SSH_TTY", s->tty);
- if (original_command)
- child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND",
- original_command);
- 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(struct ssh *ssh, Session *s, const char *shell)
- {
- FILE *f = NULL;
- char *cmd = NULL, *user_rc = NULL;
- int do_xauth;
- struct stat st;
- do_xauth =
- s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL;
- xasprintf(&user_rc, "%s/%s", s->pw->pw_dir, _PATH_SSH_USER_RC);
- /* ignore _PATH_SSH_USER_RC for subsystems and admin forced commands */
- if (!s->is_subsystem && options.adm_forced_command == NULL &&
- auth_opts->permit_user_rc && options.permit_user_rc &&
- stat(user_rc, &st) >= 0) {
- if (xasprintf(&cmd, "%s -c '%s %s'", shell, _PATH_BSHELL,
- user_rc) == -1)
- fatal("%s: xasprintf: %s", __func__, strerror(errno));
- 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",
- 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);
- }
- if (xasprintf(&cmd, "%s -q -", options.xauth_location) == -1)
- fatal("%s: xasprintf: %s", __func__, strerror(errno));
- 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);
- }
- }
- free(cmd);
- free(user_rc);
- }
- static void
- do_nologin(struct passwd *pw)
- {
- FILE *f = NULL;
- char buf[1024], *nl, *def_nl = _PATH_NOLOGIN;
- struct stat sb;
- #ifdef HAVE_LOGIN_CAP
- if (login_getcapbool(lc, "ignorenologin", 0) || pw->pw_uid == 0)
- return;
- nl = login_getcapstr(lc, "nologin", def_nl, def_nl);
- #else
- if (pw->pw_uid == 0)
- return;
- nl = def_nl;
- #endif
- if (stat(nl, &sb) == -1)
- return;
- /* /etc/nologin exists. Print its contents if we can and exit. */
- logit("User %.100s not allowed because %s exists", pw->pw_name, nl);
- if ((f = fopen(nl, "r")) != NULL) {
- while (fgets(buf, sizeof(buf), f))
- fputs(buf, stderr);
- fclose(f);
- }
- exit(254);
- }
- /*
- * Chroot into a directory after checking it for safety: all path components
- * must be root-owned directories with strict permissions.
- */
- static void
- safely_chroot(const char *path, uid_t uid)
- {
- const char *cp;
- char component[PATH_MAX];
- struct stat st;
- if (!path_absolute(path))
- fatal("chroot path does not begin at root");
- if (strlen(path) >= sizeof(component))
- fatal("chroot path too long");
- /*
- * Descend the path, checking that each component is a
- * root-owned directory with strict permissions.
- */
- for (cp = path; cp != NULL;) {
- if ((cp = strchr(cp, '/')) == NULL)
- strlcpy(component, path, sizeof(component));
- else {
- cp++;
- memcpy(component, path, cp - path);
- component[cp - path] = '\0';
- }
-
- debug3("%s: checking '%s'", __func__, component);
- if (stat(component, &st) != 0)
- fatal("%s: stat(\"%s\"): %s", __func__,
- component, strerror(errno));
- if (st.st_uid != 0 || (st.st_mode & 022) != 0)
- fatal("bad ownership or modes for chroot "
- "directory %s\"%s\"",
- cp == NULL ? "" : "component ", component);
- if (!S_ISDIR(st.st_mode))
- fatal("chroot path %s\"%s\" is not a directory",
- cp == NULL ? "" : "component ", component);
- }
- if (chdir(path) == -1)
- fatal("Unable to chdir to chroot path \"%s\": "
- "%s", path, strerror(errno));
- if (chroot(path) == -1)
- fatal("chroot(\"%s\"): %s", path, strerror(errno));
- if (chdir("/") == -1)
- fatal("%s: chdir(/) after chroot: %s",
- __func__, strerror(errno));
- verbose("Changed root directory to \"%s\"", path);
- }
- /* Set login name, uid, gid, and groups. */
- void
- do_setusercontext(struct passwd *pw)
- {
- char uidstr[32], *chroot_path, *tmp;
- platform_setusercontext(pw);
- if (platform_privileged_uidswap() && (!is_child || !use_privsep)) {
- #ifdef HAVE_LOGIN_CAP
- if (setusercontext(lc, pw, pw->pw_uid,
- (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETUSER))) < 0) {
- perror("unable to set user context");
- exit(1);
- }
- #else
- 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();
- #endif
- platform_setusercontext_post_groups(pw);
- if (!in_chroot && options.chroot_directory != NULL) { //&&
- // strcasecmp(options.chroot_directory, "none") != 0) {
- tmp = tilde_expand_filename(options.chroot_directory,
- pw->pw_uid);
- snprintf(uidstr, sizeof(uidstr), "%llu",
- (unsigned long long)pw->pw_uid);
- chroot_path = percent_expand(tmp, "h", pw->pw_dir,
- "u", pw->pw_name, "U", uidstr, (char *)NULL);
- #ifdef WITH_SELINUX
- sshd_selinux_copy_context();
- #endif
- safely_chroot(chroot_path, pw->pw_uid);
- free(tmp);
- free(chroot_path);
- /* Make sure we don't attempt to chroot again */
- free(options.chroot_directory);
- options.chroot_directory = NULL;
- in_chroot = 1;
- }
- #ifdef HAVE_LOGIN_CAP
- if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUSER) < 0) {
- perror("unable to set user context (setuser)");
- exit(1);
- }
- /*
- * FreeBSD's setusercontext() will not apply the user's
- * own umask setting unless running with the user's UID.
- */
- (void) setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUMASK);
- #else
- # ifdef USE_LIBIAF
- /*
- * In a chroot environment, the set_id() will always fail;
- * typically because of the lack of necessary authentication
- * services and runtime such as ./usr/lib/libiaf.so,
- * ./usr/lib/libpam.so.1, and ./etc/passwd We skip it in the
- * internal sftp chroot case. We'll lose auditing and ACLs but
- * permanently_set_uid will take care of the rest.
- */
- if (!in_chroot && set_id(pw->pw_name) != 0)
- fatal("set_id(%s) Failed", pw->pw_name);
- # endif /* USE_LIBIAF */
- /* Permanently switch to the desired uid. */
- permanently_set_uid(pw);
- #endif
- #ifdef WITH_SELINUX
- if (in_chroot == 0)
- sshd_selinux_copy_context();
- #endif
- } else if (options.chroot_directory != NULL &&
- strcasecmp(options.chroot_directory, "none") != 0) {
- fatal("server lacks privileges to chroot to ChrootDirectory");
- }
- 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");
- #ifdef PASSWD_NEEDS_USERNAME
- execl(_PATH_PASSWD_PROG, "passwd", s->pw->pw_name,
- (char *)NULL);
- #else
- execl(_PATH_PASSWD_PROG, "passwd", (char *)NULL);
- #endif
- perror("passwd");
- } else {
- fprintf(stderr,
- "Password change required but no TTY available.\n");
- }
- exit(1);
- }
- static void
- child_close_fds(struct ssh *ssh)
- {
- extern int auth_sock;
- if (auth_sock != -1) {
- close(auth_sock);
- auth_sock = -1;
- }
- if (ssh_packet_get_connection_in(ssh) ==
- ssh_packet_get_connection_out(ssh))
- close(ssh_packet_get_connection_in(ssh));
- else {
- close(ssh_packet_get_connection_in(ssh));
- close(ssh_packet_get_connection_out(ssh));
- }
- /*
- * Close all descriptors related to channels. They will still remain
- * open in the parent.
- */
- /* XXX better use close-on-exec? -markus */
- channel_close_all(ssh);
- /*
- * Close any extra file descriptors. Note that there may still be
- * descriptors left by system functions. They will be closed later.
- */
- endpwent();
- /* Stop directing logs to a high-numbered fd before we close it */
- log_redirect_stderr_to(NULL);
- }
- /*
- * 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.
- */
- #define ARGV_MAX 10
- void
- do_child(struct ssh *ssh, Session *s, const char *command)
- {
- extern char **environ;
- char **env, *argv[ARGV_MAX], remote_id[512];
- const char *shell, *shell0;
- struct passwd *pw = s->pw;
- int r = 0;
- sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id));
- /* remove hostkey from the child's memory */
- destroy_sensitive_data(ssh, 1);
- ssh_packet_clear_keys(ssh);
- /* Don't audit this - both us and the parent would be talking to the
- monitor over a single socket, with no synchronization. */
- packet_destroy_all(ssh, 0, 1);
- /* Force a password change */
- if (s->authctxt->force_pwchange) {
- do_setusercontext(pw);
- child_close_fds(ssh);
- do_pwchange(s);
- exit(1);
- }
- /*
- * Login(1) does this as well, and it needs uid 0 for the "-h"
- * switch, so we let login(1) to this for us.
- */
- #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 */
- /* When PAM is enabled we rely on it to do the nologin check */
- if (!options.use_pam)
- 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 (!check_quietlogin(s, command))
- display_loginmsg();
- #endif /* HAVE_OSF_SIA */
- #ifdef USE_PAM
- if (options.use_pam && !is_pam_session_open()) {
- debug3("PAM session not opened, exiting");
- display_loginmsg();
- exit(254);
- }
- #endif
- /*
- * 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;
- /*
- * Make sure $SHELL points to the shell from the password file,
- * even if shell is overridden from login.conf
- */
- env = do_setup_env(ssh, s, shell);
- #ifdef HAVE_LOGIN_CAP
- shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell);
- #endif
- /*
- * 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
- * ssh_remote_ipaddr there.
- */
- child_close_fds(ssh);
- /*
- * 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
- /* Change current directory to the user's home directory. */
- if (chdir(pw->pw_dir) == -1) {
- /* Suppress missing homedir warning for chroot case */
- #ifdef HAVE_LOGIN_CAP
- r = login_getcapbool(lc, "requirehome", 0);
- #endif
- if (r || !in_chroot) {
- fprintf(stderr, "Could not chdir to home "
- "directory %s: %s\n", pw->pw_dir,
- strerror(errno));
- }
- if (r)
- exit(1);
- }
- do_rc_files(ssh, s, shell);
- /* restore SIGPIPE for child */
- ssh_signal(SIGPIPE, SIG_DFL);
- if (s->is_subsystem == SUBSYSTEM_INT_SFTP_ERROR) {
- error("Connection from %s: refusing non-sftp session",
- remote_id);
- printf("This service allows sftp connections only.\n");
- fflush(NULL);
- exit(1);
- } else if (s->is_subsystem == SUBSYSTEM_INT_SFTP) {
- extern int optind, optreset;
- int i;
- char *p, *args;
- setproctitle("%s@%s", s->pw->pw_name, INTERNAL_SFTP_NAME);
- args = xstrdup(command ? command : "sftp-server");
- for (i = 0, (p = strtok(args, " ")); p; (p = strtok(NULL, " ")))
- if (i < ARGV_MAX - 1)
- argv[i++] = p;
- argv[i] = NULL;
- optind = optreset = 1;
- __progname = argv[0];
- exit(sftp_server_main(i, argv, s->pw, have_dev_log));
- }
- fflush(NULL);
- /* 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);
- }
- void
- session_unused(int id)
- {
- debug3("%s: session id %d unused", __func__, id);
- if (id >= options.max_sessions ||
- id >= sessions_nalloc) {
- fatal("%s: insane session id %d (max %d nalloc %d)",
- __func__, id, options.max_sessions, sessions_nalloc);
- }
- memset(&sessions[id], 0, sizeof(*sessions));
- sessions[id].self = id;
- sessions[id].used = 0;
- sessions[id].chanid = -1;
- sessions[id].ptyfd = -1;
- sessions[id].ttyfd = -1;
- sessions[id].ptymaster = -1;
- sessions[id].x11_chanids = NULL;
- #ifdef SSH_AUDIT_EVENTS
- sessions[id].command_handle = -1;
- #endif
- sessions[id].next_unused = sessions_first_unused;
- sessions_first_unused = id;
- }
- Session *
- session_new(void)
- {
- Session *s, *tmp;
- if (sessions_first_unused == -1) {
- if (sessions_nalloc >= options.max_sessions)
- return NULL;
- debug2("%s: allocate (allocated %d max %d)",
- __func__, sessions_nalloc, options.max_sessions);
- tmp = xrecallocarray(sessions, sessions_nalloc,
- sessions_nalloc + 1, sizeof(*sessions));
- if (tmp == NULL) {
- error("%s: cannot allocate %d sessions",
- __func__, sessions_nalloc + 1);
- return NULL;
- }
- sessions = tmp;
- session_unused(sessions_nalloc++);
- }
- if (sessions_first_unused >= sessions_nalloc ||
- sessions_first_unused < 0) {
- fatal("%s: insane first_unused %d max %d nalloc %d",
- __func__, sessions_first_unused, options.max_sessions,
- sessions_nalloc);
- }
- s = &sessions[sessions_first_unused];
- if (s->used) {
- fatal("%s: session %d already used",
- __func__, sessions_first_unused);
- }
- sessions_first_unused = s->next_unused;
- s->used = 1;
- s->next_unused = -1;
- debug("session_new: session %d", s->self);
- return s;
- }
- static void
- session_dump(void)
- {
- int i;
- for (i = 0; i < sessions_nalloc; i++) {
- Session *s = &sessions[i];
- debug("dump: used %d next_unused %d session %d %p "
- "channel %d pid %ld",
- s->used,
- s->next_unused,
- 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_id(int id)
- {
- if (id >= 0 && id < sessions_nalloc) {
- Session *s = &sessions[id];
- if (s->used)
- return s;
- }
- debug("%s: unknown id %d", __func__, id);
- session_dump();
- return NULL;
- }
- Session *
- session_by_tty(char *tty)
- {
- int i;
- for (i = 0; i < sessions_nalloc; 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 < sessions_nalloc; 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_x11_channel(int id)
- {
- int i, j;
- for (i = 0; i < sessions_nalloc; i++) {
- Session *s = &sessions[i];
- if (s->x11_chanids == NULL || !s->used)
- continue;
- for (j = 0; s->x11_chanids[j] != -1; j++) {
- if (s->x11_chanids[j] == id) {
- debug("session_by_x11_channel: session %d "
- "channel %d", s->self, id);
- return s;
- }
- }
- }
- debug("session_by_x11_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 < sessions_nalloc; 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(struct ssh *ssh, Session *s)
- {
- int r;
- if ((r = sshpkt_get_u32(ssh, &s->col)) != 0 ||
- (r = sshpkt_get_u32(ssh, &s->row)) != 0 ||
- (r = sshpkt_get_u32(ssh, &s->xpixel)) != 0 ||
- (r = sshpkt_get_u32(ssh, &s->ypixel)) != 0 ||
- (r = sshpkt_get_end(ssh)) != 0)
- sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
- pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
- return 1;
- }
- static int
- session_pty_req(struct ssh *ssh, Session *s)
- {
- int r;
- if (!auth_opts->permit_pty_flag || !options.permit_tty) {
- debug("Allocating a pty not permitted for this connection.");
- return 0;
- }
- if (s->ttyfd != -1) {
- ssh_packet_disconnect(ssh, "Protocol error: you already have a pty.");
- return 0;
- }
- if ((r = sshpkt_get_cstring(ssh, &s->term, NULL)) != 0 ||
- (r = sshpkt_get_u32(ssh, &s->col)) != 0 ||
- (r = sshpkt_get_u32(ssh, &s->row)) != 0 ||
- (r = sshpkt_get_u32(ssh, &s->xpixel)) != 0 ||
- (r = sshpkt_get_u32(ssh, &s->ypixel)) != 0)
- sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
- if (strcmp(s->term, "") == 0) {
- free(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)))) {
- free(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);
- ssh_tty_parse_modes(ssh, s->ttyfd);
- if ((r = sshpkt_get_end(ssh)) != 0)
- sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
- 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);
- session_proctitle(s);
- return 1;
- }
- static int
- session_subsystem_req(struct ssh *ssh, Session *s)
- {
- struct stat st;
- int r, success = 0;
- char *prog, *cmd;
- u_int i;
- if ((r = sshpkt_get_cstring(ssh, &s->subsys, NULL)) != 0 ||
- (r = sshpkt_get_end(ssh)) != 0)
- sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
- debug2("subsystem request for %.100s by user %s", s->subsys,
- s->pw->pw_name);
- for (i = 0; i < options.num_subsystems; i++) {
- if (strcmp(s->subsys, options.subsystem_name[i]) == 0) {
- prog = options.subsystem_command[i];
- cmd = options.subsystem_args[i];
- if (strcmp(INTERNAL_SFTP_NAME, prog) == 0) {
- s->is_subsystem = SUBSYSTEM_INT_SFTP;
- debug("subsystem: %s", prog);
- } else {
- if (stat(prog, &st) == -1)
- debug("subsystem: cannot stat %s: %s",
- prog, strerror(errno));
- s->is_subsystem = SUBSYSTEM_EXT;
- debug("subsystem: exec() %s", cmd);
- }
- success = do_exec(ssh, s, cmd) == 0;
- break;
- }
- }
- if (!success)
- logit("subsystem request for %.100s by user %s failed, "
- "subsystem not found", s->subsys, s->pw->pw_name);
- return success;
- }
- static int
- session_x11_req(struct ssh *ssh, Session *s)
- {
- int r, success;
- u_char single_connection = 0;
- if (s->auth_proto != NULL || s->auth_data != NULL) {
- error("session_x11_req: session %d: "
- "x11 forwarding already active", s->self);
- return 0;
- }
- if ((r = sshpkt_get_u8(ssh, &single_connection)) != 0 ||
- (r = sshpkt_get_cstring(ssh, &s->auth_proto, NULL)) != 0 ||
- (r = sshpkt_get_cstring(ssh, &s->auth_data, NULL)) != 0 ||
- (r = sshpkt_get_u32(ssh, &s->screen)) != 0 ||
- (r = sshpkt_get_end(ssh)) != 0)
- sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
- s->single_connection = single_connection;
- if (xauth_valid_string(s->auth_proto) &&
- xauth_valid_string(s->auth_data))
- success = session_setup_x11fwd(ssh, s);
- else {
- success = 0;
- error("Invalid X11 forwarding data");
- }
- if (!success) {
- free(s->auth_proto);
- free(s->auth_data);
- s->auth_proto = NULL;
- s->auth_data = NULL;
- }
- return success;
- }
- static int
- session_shell_req(struct ssh *ssh, Session *s)
- {
- int r;
- if ((r = sshpkt_get_end(ssh)) != 0)
- sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
- return do_exec(ssh, s, NULL) == 0;
- }
- static int
- session_exec_req(struct ssh *ssh, Session *s)
- {
- u_int success;
- int r;
- char *command = NULL;
- if ((r = sshpkt_get_cstring(ssh, &command, NULL)) != 0 ||
- (r = sshpkt_get_end(ssh)) != 0)
- sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
- success = do_exec(ssh, s, command) == 0;
- free(command);
- return success;
- }
- static int
- session_break_req(struct ssh *ssh, Session *s)
- {
- int r;
- if ((r = sshpkt_get_u32(ssh, NULL)) != 0 || /* ignore */
- (r = sshpkt_get_end(ssh)) != 0)
- sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
- if (s->ptymaster == -1 || tcsendbreak(s->ptymaster, 0) == -1)
- return 0;
- return 1;
- }
- static int
- session_env_req(struct ssh *ssh, Session *s)
- {
- char *name, *val;
- u_int i;
- int r;
- if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0 ||
- (r = sshpkt_get_cstring(ssh, &val, NULL)) != 0 ||
- (r = sshpkt_get_end(ssh)) != 0)
- sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
- /* 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 = xrecallocarray(s->env, s->num_env,
- s->num_env + 1, sizeof(*s->env));
- 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:
- free(name);
- free(val);
- return (0);
- }
- /*
- * Conversion of signals from ssh channel request names.
- * Subset of signals from RFC 4254 section 6.10C, with SIGINFO as
- * local extension.
- */
- static int
- name2sig(char *name)
- {
- #define SSH_SIG(x) if (strcmp(name, #x) == 0) return SIG ## x
- SSH_SIG(HUP);
- SSH_SIG(INT);
- SSH_SIG(KILL);
- SSH_SIG(QUIT);
- SSH_SIG(TERM);
- SSH_SIG(USR1);
- SSH_SIG(USR2);
- #undef SSH_SIG
- #ifdef SIGINFO
- if (strcmp(name, "INFO@openssh.com") == 0)
- return SIGINFO;
- #endif
- return -1;
- }
- static int
- session_signal_req(struct ssh *ssh, Session *s)
- {
- char *signame = NULL;
- int r, sig, success = 0;
- if ((r = sshpkt_get_cstring(ssh, &signame, NULL)) != 0 ||
- (r = sshpkt_get_end(ssh)) != 0) {
- error("%s: parse packet: %s", __func__, ssh_err(r));
- goto out;
- }
- if ((sig = name2sig(signame)) == -1) {
- error("%s: unsupported signal \"%s\"", __func__, signame);
- goto out;
- }
- if (s->pid <= 0) {
- error("%s: no pid for session %d", __func__, s->self);
- goto out;
- }
- if (s->forced || s->is_subsystem) {
- error("%s: refusing to send signal %s to %s session", __func__,
- signame, s->forced ? "forced-command" : "subsystem");
- goto out;
- }
- if (!use_privsep || mm_is_monitor()) {
- error("%s: session signalling requires privilege separation",
- __func__);
- goto out;
- }
- debug("%s: signal %s, killpg(%ld, %d)", __func__, signame,
- (long)s->pid, sig);
- temporarily_use_uid(s->pw);
- r = killpg(s->pid, sig);
- restore_uid();
- if (r != 0) {
- error("%s: killpg(%ld, %d): %s", __func__, (long)s->pid,
- sig, strerror(errno));
- goto out;
- }
- /* success */
- success = 1;
- out:
- free(signame);
- return success;
- }
- static int
- session_auth_agent_req(struct ssh *ssh, Session *s)
- {
- static int called = 0;
- int r;
- if ((r = sshpkt_get_end(ssh)) != 0)
- sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
- if (!auth_opts->permit_agent_forwarding_flag ||
- !options.allow_agent_forwarding) {
- debug("%s: agent forwarding disabled", __func__);
- return 0;
- }
- if (called) {
- return 0;
- } else {
- called = 1;
- return auth_input_request_forwarding(ssh, s->pw);
- }
- }
- int
- session_input_channel_req(struct ssh *ssh, Channel *c, const char *rtype)
- {
- int success = 0;
- Session *s;
- if ((s = session_by_channel(c->self)) == NULL) {
- logit("%s: no session %d req %.100s", __func__, c->self, rtype);
- return 0;
- }
- debug("%s: session %d req %s", __func__, 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(ssh, s);
- } else if (strcmp(rtype, "exec") == 0) {
- success = session_exec_req(ssh, s);
- } else if (strcmp(rtype, "pty-req") == 0) {
- success = session_pty_req(ssh, s);
- } else if (strcmp(rtype, "x11-req") == 0) {
- success = session_x11_req(ssh, s);
- } else if (strcmp(rtype, "auth-agent-req@openssh.com") == 0) {
- success = session_auth_agent_req(ssh, s);
- } else if (strcmp(rtype, "subsystem") == 0) {
- success = session_subsystem_req(ssh, s);
- } else if (strcmp(rtype, "env") == 0) {
- success = session_env_req(ssh, s);
- }
- }
- if (strcmp(rtype, "window-change") == 0) {
- success = session_window_change_req(ssh, s);
- } else if (strcmp(rtype, "break") == 0) {
- success = session_break_req(ssh, s);
- } else if (strcmp(rtype, "signal") == 0) {
- success = session_signal_req(ssh, s);
- }
- return success;
- }
- void
- session_set_fds(struct ssh *ssh, Session *s,
- int fdin, int fdout, int fderr, int ignore_fderr, int is_tty)
- {
- /*
- * 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(ssh, s->chanid,
- fdout, fdin, fderr,
- ignore_fderr ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,
- 1, is_tty,
- options.hpn_disabled ? CHAN_SES_WINDOW_DEFAULT : options.hpn_buffer_size);
- }
- /*
- * 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("%s: no session", __func__);
- return;
- }
- if (s->ttyfd == -1)
- return;
- debug("%s: session %d release %s", __func__, 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 (s->ptymaster != -1 && close(s->ptymaster) == -1)
- 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@openssh.com";
- }
- static void
- session_close_x11(struct ssh *ssh, int id)
- {
- Channel *c;
- if ((c = channel_by_id(ssh, id)) == NULL) {
- debug("%s: x11 channel %d missing", __func__, id);
- } else {
- /* Detach X11 listener */
- debug("%s: detach x11 channel %d", __func__, id);
- channel_cancel_cleanup(ssh, id);
- if (c->ostate != CHAN_OUTPUT_CLOSED)
- chan_mark_dead(ssh, c);
- }
- }
- static void
- session_close_single_x11(struct ssh *ssh, int id, void *arg)
- {
- Session *s;
- u_int i;
- debug3("%s: channel %d", __func__, id);
- channel_cancel_cleanup(ssh, id);
- if ((s = session_by_x11_channel(id)) == NULL)
- fatal("%s: no x11 channel %d", __func__, id);
- for (i = 0; s->x11_chanids[i] != -1; i++) {
- debug("%s: session %d: closing channel %d",
- __func__, s->self, s->x11_chanids[i]);
- /*
- * The channel "id" is already closing, but make sure we
- * close all of its siblings.
- */
- if (s->x11_chanids[i] != id)
- session_close_x11(ssh, s->x11_chanids[i]);
- }
- free(s->x11_chanids);
- s->x11_chanids = NULL;
- free(s->display);
- s->display = NULL;
- free(s->auth_proto);
- s->auth_proto = NULL;
- free(s->auth_data);
- s->auth_data = NULL;
- free(s->auth_display);
- s->auth_display = NULL;
- }
- static void
- session_exit_message(struct ssh *ssh, Session *s, int status)
- {
- Channel *c;
- int r;
- if ((c = channel_lookup(ssh, s->chanid)) == NULL)
- fatal("%s: session %d: no channel %d",
- __func__, s->self, s->chanid);
- debug("%s: session %d channel %d pid %ld",
- __func__, s->self, s->chanid, (long)s->pid);
- if (WIFEXITED(status)) {
- channel_request_start(ssh, s->chanid, "exit-status", 0);
- if ((r = sshpkt_put_u32(ssh, WEXITSTATUS(status))) != 0 ||
- (r = sshpkt_send(ssh)) != 0)
- sshpkt_fatal(ssh, r, "%s: exit reply", __func__);
- } else if (WIFSIGNALED(status)) {
- channel_request_start(ssh, s->chanid, "exit-signal", 0);
- #ifndef WCOREDUMP
- # define WCOREDUMP(x) (0)
- #endif
- if ((r = sshpkt_put_cstring(ssh, sig2name(WTERMSIG(status)))) != 0 ||
- (r = sshpkt_put_u8(ssh, WCOREDUMP(status)? 1 : 0)) != 0 ||
- (r = sshpkt_put_cstring(ssh, "")) != 0 ||
- (r = sshpkt_put_cstring(ssh, "")) != 0 ||
- (r = sshpkt_send(ssh)) != 0)
- sshpkt_fatal(ssh, r, "%s: exit reply", __func__);
- } else {
- /* Some weird exit cause. Just exit. */
- ssh_packet_disconnect(ssh, "wait returned status %04x.", status);
- }
- /* disconnect channel */
- debug("%s: release channel %d", __func__, s->chanid);
- /*
- * Adjust cleanup callback attachment to send close messages when
- * the channel gets EOF. The session will be then be closed
- * by session_close_by_channel when the child sessions close their fds.
- */
- channel_register_cleanup(ssh, c->self, session_close_by_channel, 1);
- /*
- * 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(ssh, c);
- }
- #ifdef SSH_AUDIT_EVENTS
- void
- session_end_command2(struct ssh *ssh, Session *s)
- {
- if (s->command != NULL) {
- if (s->command_handle != -1)
- audit_end_command(ssh, s->command_handle, s->command);
- free(s->command);
- s->command = NULL;
- s->command_handle = -1;
- }
- }
- static void
- session_end_command(struct ssh *ssh, Session *s)
- {
- if (s->command != NULL) {
- if (s->command_handle != -1)
- PRIVSEP(audit_end_command(ssh, s->command_handle, s->command));
- free(s->command);
- s->command = NULL;
- s->command_handle = -1;
- }
- }
- #endif
- void
- session_close(struct ssh *ssh, Session *s)
- {
- u_int i;
- int do_xauth;
- verbose("Close session: user %s from %.200s port %d id %d",
- s->pw->pw_name,
- ssh_remote_ipaddr(ssh),
- ssh_remote_port(ssh),
- s->self);
- do_xauth = s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL;
- if (do_xauth && options.xauth_location != NULL) {
- pid_t pid;
- FILE *f;
- char cmd[1024];
- struct passwd * pw = s->pw;
- if (!(pid = fork())) {
- permanently_set_uid(pw);
- /* Remove authority data from .Xauthority if appropriate. */
- debug("Running %.500s remove %.100s\n",
- options.xauth_location, s->auth_display);
-
- snprintf(cmd, sizeof cmd, "unset XAUTHORITY && HOME=\"%.200s\" %s -q -",
- s->pw->pw_dir, options.xauth_location);
- f = popen(cmd, "w");
- if (f) {
- fprintf(f, "remove %s\n", s->auth_display);
- pclose(f);
- } else
- error("Could not run %s\n", cmd);
- exit(0);
- } else if (pid > 0) {
- int status;
- waitpid(pid, &status, 0);
- }
- }
- if (s->ttyfd != -1)
- session_pty_cleanup(s);
- #ifdef SSH_AUDIT_EVENTS
- if (s->command)
- session_end_command(ssh, s);
- #endif
- free(s->term);
- free(s->display);
- free(s->x11_chanids);
- free(s->auth_display);
- free(s->auth_data);
- free(s->auth_proto);
- free(s->subsys);
- if (s->env != NULL) {
- for (i = 0; i < s->num_env; i++) {
- free(s->env[i].name);
- free(s->env[i].val);
- }
- free(s->env);
- }
- session_proctitle(s);
- session_unused(s->self);
- }
- void
- session_close_by_pid(struct ssh *ssh, pid_t pid, int status)
- {
- Session *s = session_by_pid(pid);
- if (s == NULL) {
- debug("%s: no session for pid %ld", __func__, (long)pid);
- return;
- }
- if (s->chanid != -1)
- session_exit_message(ssh, s, status);
- if (s->ttyfd != -1)
- session_pty_cleanup(s);
- s->pid = 0;
- }
- /*
- * this is called when a channel dies before
- * the session 'child' itself dies
- */
- void
- session_close_by_channel(struct ssh *ssh, int id, void *arg)
- {
- Session *s = session_by_channel(id);
- u_int i;
- if (s == NULL) {
- debug("%s: no session for id %d", __func__, id);
- return;
- }
- debug("%s: channel %d child %ld", __func__, id, (long)s->pid);
- if (s->pid != 0) {
- debug("%s: channel %d: has child, ttyfd %d",
- __func__, id, s->ttyfd);
- /*
- * 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(ssh, s->chanid);
- /* Close any X11 listeners associated with this session */
- if (s->x11_chanids != NULL) {
- for (i = 0; s->x11_chanids[i] != -1; i++) {
- session_close_x11(ssh, s->x11_chanids[i]);
- s->x11_chanids[i] = -1;
- }
- }
- s->chanid = -1;
- session_close(ssh, s);
- }
- void
- session_destroy_all(struct ssh *ssh, void (*closefunc)(struct ssh *ssh, Session *))
- {
- int i;
- for (i = 0; i < sessions_nalloc; i++) {
- Session *s = &sessions[i];
- if (s->used) {
- if (closefunc != NULL)
- closefunc(ssh, s);
- else
- session_close(ssh, s);
- }
- }
- }
- static char *
- session_tty_list(void)
- {
- static char buf[1024];
- int i;
- char *cp;
- buf[0] = '\0';
- for (i = 0; i < sessions_nalloc; 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(struct ssh *ssh, Session *s)
- {
- struct stat st;
- char display[512], auth_display[512];
- char hostname[NI_MAXHOST];
- u_int i;
- if (!auth_opts->permit_x11_forwarding_flag) {
- ssh_packet_send_debug(ssh, "X11 forwarding disabled by key options.");
- return 0;
- }
- if (!options.x11_forwarding) {
- debug("X11 forwarding disabled in server configuration file.");
- return 0;
- }
- if (options.xauth_location == NULL ||
- (stat(options.xauth_location, &st) == -1)) {
- ssh_packet_send_debug(ssh, "No xauth program; cannot forward X11.");
- return 0;
- }
- if (s->display != NULL) {
- debug("X11 display already set.");
- return 0;
- }
- if (x11_create_display_inet(ssh, options.x11_display_offset,
- options.x11_use_localhost, s->single_connection,
- &s->display_number, &s->x11_chanids) == -1) {
- debug("x11_create_display_inet failed.");
- return 0;
- }
- for (i = 0; s->x11_chanids[i] != -1; i++) {
- channel_register_cleanup(ssh, s->x11_chanids[i],
- session_close_single_x11, 0);
- }
- /* Set up a suitable value for the DISPLAY variable. */
- if (gethostname(hostname, sizeof(hostname)) == -1)
- 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.");
- ssh_packet_send_debug(ssh, "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(struct ssh *ssh, Authctxt *authctxt)
- {
- server_loop2(ssh, authctxt);
- }
- static void
- do_cleanup_one_session(struct ssh *ssh, Session *s)
- {
- session_pty_cleanup2(s);
- #ifdef SSH_AUDIT_EVENTS
- session_end_command2(ssh, s);
- #endif
- }
- void
- do_cleanup(struct ssh *ssh, 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 USE_PAM
- if (options.use_pam) {
- sshpam_cleanup();
- sshpam_thread_cleanup();
- }
- #endif
- if (!authctxt->authenticated)
- return;
- #ifdef KRB5
- if (options.kerberos_ticket_cleanup &&
- authctxt->krb5_ctx)
- krb5_cleanup_proc(authctxt);
- #endif
- #ifdef GSSAPI
- if (options.gss_cleanup_creds)
- ssh_gssapi_cleanup_creds();
- #endif
- /* remove agent socket */
- auth_sock_cleanup_proc(authctxt->pw);
- /* remove userauth info */
- if (auth_info_file != NULL) {
- temporarily_use_uid(authctxt->pw);
- unlink(auth_info_file);
- restore_uid();
- free(auth_info_file);
- auth_info_file = NULL;
- }
- /*
- * Cleanup ptys/utmp only if privsep is disabled,
- * or if running in monitor.
- */
- if (!use_privsep || mm_is_monitor())
- session_destroy_all(ssh, do_cleanup_one_session);
- }
- /* Return a name for the remote host that fits inside utmp_size */
- const char *
- session_get_remote_name_or_ip(struct ssh *ssh, u_int utmp_size, int use_dns)
- {
- const char *remote = "";
- if (utmp_size > 0)
- remote = auth_get_canonical_hostname(ssh, use_dns);
- if (utmp_size == 0 || strlen(remote) > utmp_size)
- remote = ssh_remote_ipaddr(ssh);
- return remote;
- }
|