PageRenderTime 92ms CodeModel.GetById 43ms app.highlight 41ms RepoModel.GetById 1ms app.codeStats 0ms

/src/qsnet/qshell.c

https://code.google.com/
C | 969 lines | 638 code | 149 blank | 182 comment | 154 complexity | 9faafc3236b7b1f2c2db1b1b18bb5f6c MD5 | raw file
  1/*****************************************************************************\
  2 *  $Id$
  3 *****************************************************************************
  4 *  Copyright (C) 2001-2006 The Regents of the University of California.
  5 *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
  6 *  Written by Jim Garlick <garlick@llnl.gov>.
  7 *  UCRL-CODE-2003-005.
  8 *  
  9 *  This file is part of Pdsh, a parallel remote shell program.
 10 *  For details, see <http://www.llnl.gov/linux/pdsh/>.
 11 *  
 12 *  Pdsh is free software; you can redistribute it and/or modify it under
 13 *  the terms of the GNU General Public License as published by the Free
 14 *  Software Foundation; either version 2 of the License, or (at your option)
 15 *  any later version.
 16 *  
 17 *  Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY
 18 *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 19 *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 20 *  details.
 21 *  
 22 *  You should have received a copy of the GNU General Public License along
 23 *  with Pdsh; if not, write to the Free Software Foundation, Inc.,
 24 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 25 \*****************************************************************************/
 26
 27/*-
 28 * Started with BSD rshd which is:
 29 *
 30 * Copyright (c) 1988, 1989 The Regents of the University of California.
 31 * All rights reserved.
 32 *
 33 * Redistribution and use in source and binary forms, with or without
 34 * modification, are permitted provided that the following conditions
 35 * are met:
 36 * 1. Redistributions of source code must retain the above copyright
 37 *    notice, this list of conditions and the following disclaimer.
 38 *
 39 * 2. Redistributions in binary form must reproduce the above copyright
 40 *    notice, this list of conditions and the following disclaimer in the
 41 *    documentation and/or other materials provided with the distribution.
 42 *
 43 * 3. All advertising materials mentioning features or use of this software
 44 *    must display the following acknowledgement:
 45 *      This product includes software developed by the University of
 46 *      California, Berkeley and its contributors.
 47 *
 48 * 4. Neither the name of the University nor the names of its contributors
 49 *    may be used to endorse or promote products derived from this software
 50 *    without specific prior written permission.
 51 *
 52 * 5. This is free software; you can redistribute it and/or modify it
 53 *    under the terms of the GNU General Public License as published
 54 *    by the Free Software Foundation; either version 2 of the
 55 *    License, or (at your option) any later version.
 56 *                              
 57 * 6. This is distributed in the hope that it will be useful, but
 58 *    WITHOUT ANY WARRANTY; without even the implied warranty of
 59 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 60 *    GNU General Public License for more details.
 61 *                                                           
 62 * 7. You should have received a copy of the GNU General Public License;
 63 *    if not, write to the Free Software Foundation, Inc., 59 Temple
 64 *    Place, Suite 330, Boston, MA  02111-1307  USA.
 65 *
 66 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 67 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 68 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 69 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 70 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 71 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 72 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 73 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 74 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 75 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 76 * SUCH DAMAGE.
 77 */
 78
 79/*
 80 * PAM modifications by Michael K. Johnson <johnsonm@redhat.com>
 81 */
 82
 83#if     HAVE_CONFIG_H
 84#include "config.h"
 85#endif
 86
 87#include <sys/types.h>
 88#include <sys/param.h>
 89#include <sys/ioctl.h>
 90#include <sys/time.h>
 91#include <sys/wait.h>
 92#include <fcntl.h>
 93#include <signal.h>
 94#include <sys/select.h>
 95
 96#include <sys/socket.h>
 97#include <netinet/in.h>
 98#include <arpa/inet.h>
 99#include <netdb.h>
100
101#include <pwd.h>
102#include <grp.h>
103#include <sys/syslog.h>
104#include <resolv.h>
105#if HAVE_UNISTD_H
106#include <unistd.h>
107#endif
108
109#include <errno.h>
110#include <stdio.h>
111#include <stdlib.h>
112#include <string.h>
113#include <paths.h>
114#include <stdarg.h>
115#include <ctype.h>
116#include <assert.h>
117
118#include "src/common/xmalloc.h"
119#include "src/common/xstring.h"
120#include "src/common/err.h"
121#include "src/common/fd.h"
122#include "src/qsnet/qswutil.h"
123#include "src/qsnet/qshell.h"
124
125int keepalive = 1;
126int check_all = 0;
127int paranoid = 0;
128int sent_null = 0;
129int allow_root_rhosts = 1;
130
131static char typename[BUFSIZ];
132
133#define OPTIONS "ahlLn"
134
135#if defined(__GLIBC__) && (__GLIBC__ >= 2)
136#define _check_rhosts_file  __check_rhosts_file
137#endif
138
139extern int _check_rhosts_file;
140
141#ifdef USE_PAM
142#include <security/pam_appl.h>
143#include <security/pam_misc.h>
144#include "src/common/list.h"
145pam_handle_t *pamh;
146static char *last_pam_msg = NULL;
147
148#define PAM_ERRMSGLEN 4096
149char pam_errmsgbuf[PAM_ERRMSGLEN];
150const char *pam_errmsg = NULL;
151#endif
152
153char username[20] = "USER=";
154char homedir[64] = "HOME=";
155char shell[64] = "SHELL=";
156char path[100] = "PATH=";
157char *envinit[] = { homedir, shell, path, username, 0 };
158extern char **environ;
159
160static void errcommon(int, const char *, va_list, int);
161static int  readchar(char *);
162static int  getint(void);
163static int  get_qshell_info(ELAN_CAPABILITY *, qsw_info_t *, char *, int);
164static void stderr_parent(int, int, int);
165static void network_init(int, struct sockaddr_in *, int);
166
167void errcommon(int fd, const char *fmt, va_list ap, int log) {
168    char buf[BUFSIZ], *bp = buf;
169
170    if (sent_null == 0) *bp++ = 1;
171    vsnprintf(bp, sizeof(buf) - 1, fmt, ap);
172    if (log)
173        syslog(LOG_ERR, (sent_null == 0) ? buf + 1 : buf);
174    fd_write_n(fd, buf, strlen(buf));
175}
176
177void error(const char *fmt, ...) {
178    va_list ap;
179    va_start(ap, fmt);
180    errcommon(2, fmt, ap, 0);
181    va_end(ap);
182}
183
184void errlog(const char *fmt, ...) {
185    va_list ap;
186    va_start(ap, fmt);
187    errcommon(2, fmt, ap, 1);
188    va_end(ap);
189}
190
191int readchar(char *err) {
192    int rv;
193    char c;
194
195    if ((rv = read(0, &c, 1)) < 0) {
196        errlog("%s: read %s error: %s\n", typename, err, strerror(errno));
197        return -1;
198    }
199
200    if (rv != 1) {
201        errlog("%s: %s read wrong number of bytes: %d\n", typename, err, rv);
202        return -1;
203    }
204
205    return (int)c;
206}
207
208int getstr(char *buf, int cnt, char *err) {
209    int rv;
210
211    do {
212        if ((rv = readchar(err)) == -1)
213            return -1;
214        *buf++ = (char)rv;
215
216        if (--cnt == 0) {
217            errlog("%s: %s too long\n", typename, err);
218            return -1;
219        }
220    } while (rv != 0);
221
222    return cnt;
223}
224
225int getint(void) {
226    /* achu: Must fatally exit here without message back to user.  No
227     * port to connect on stderr, and a write on stdout calls
228     * xrcmd/mcmd to fail in xpoll.
229     */
230    int port = 0;
231    char c;
232    do {
233        /* achu: Can't use readchar, for above reason */
234        if (read(0, &c, 1) != 1) {
235            syslog(LOG_ERR, "read port: %m");
236            exit(1);
237        }
238        if (isascii(c) && isdigit(c)) 
239            port = port*10 + c-'0';
240    } while (c != 0);
241    return port;
242}
243
244static void copy_passwd_struct(struct passwd *to, struct passwd *from)
245{
246    to->pw_uid    = from->pw_uid;
247    to->pw_gid    = from->pw_gid;
248    to->pw_name   = strdup(from->pw_name);
249    to->pw_passwd = strdup(from->pw_passwd);
250    to->pw_gecos  = strdup(from->pw_gecos);
251    to->pw_dir    = strdup(from->pw_dir);
252    to->pw_shell  = strdup(from->pw_shell);
253
254    return;
255}
256
257/*
258 *  Get a *copy* of passwd struct for user ``user''
259 */
260struct passwd *getpwnam_common(char *user) {
261    struct passwd *pwd;
262    struct passwd *storage;
263
264    setpwent();
265    if ((pwd= getpwnam(user)) == NULL)
266        return NULL;
267    if (pwd->pw_uid != 0 && !access(_PATH_NOLOGIN, F_OK))
268        return NULL;
269    if (pwd->pw_uid == 0 || !strcmp(user, "root"))
270        paranoid = 1;
271
272    storage = Malloc(sizeof(struct passwd));
273
274    copy_passwd_struct(storage, pwd);
275
276    return storage;
277}
278
279char *findhostname(struct sockaddr_in *fromp) {
280    struct hostent *hp;
281    char *hostname;
282    char *addr = (char *)&fromp->sin_addr;
283    int inaddrsize = sizeof(struct in_addr);
284
285    if ((hp = gethostbyaddr(addr, inaddrsize, fromp->sin_family)) == NULL)
286        hostname = strdup(inet_ntoa(fromp->sin_addr));
287    else
288        hostname = strdup(hp->h_name);
289
290    if (hostname == NULL) {
291        syslog(LOG_ERR, "System out of memory");
292        return NULL;
293    }
294
295    /*
296     * Attempt to confirm the DNS.
297     */
298#ifdef  RES_DNSRCH
299    _res.options &= ~RES_DNSRCH;
300#endif
301    if ((hp = gethostbyname(hostname)) == NULL) {
302        syslog(LOG_INFO | LOG_AUTH, "Couldn't look up address for %s", hostname);
303        return NULL;
304    }
305    while (hp->h_addr_list[0] != NULL) {
306        if (!memcmp(hp->h_addr_list[0], addr, sizeof(fromp->sin_addr))) {
307            return hostname;
308        }
309        hp->h_addr_list++;
310    }
311    syslog(LOG_NOTICE | LOG_INFO | LOG_AUTH, "Host addr %s not listed for host %s", 
312            inet_ntoa(fromp->sin_addr), hp->h_name);
313    return NULL;
314}
315
316#ifdef USE_PAM
317int qshell_conv(int num_msg, const struct pam_message **msg,
318                struct pam_response **resp, void *appdata_ptr) {
319
320    /* rcmd/mqcmd requires that no data come over the stdout connection
321     * before a separate stderr connection has been made.  Therefore,
322     * we cannot send PAM_ERROR_MSG or PAM_TEXT_INFO over stdout.
323     * Instead, we will store all messages in a list until we are
324     * assured of a PAM_FAILURE or PAM_SUCCESS.
325     */
326  
327    int i = 0;
328    struct pam_response *reply;
329
330    if (num_msg <= 0)
331        return PAM_CONV_ERR;
332
333    reply = (struct pam_response *)malloc(num_msg*sizeof(struct pam_response));
334    if (!reply)
335        return PAM_CONV_ERR;
336
337    for (i = 0; i < num_msg; i++) {
338        char *string = NULL;
339
340        switch (msg[i]->msg_style) {
341        case PAM_ERROR_MSG:
342        case PAM_TEXT_INFO:
343        {
344            char *str = NULL;
345            List pam_msgs = *((List *)appdata_ptr);
346            
347            if (!(msg[i]->msg))
348                return -1;
349
350            if ((str = strdup(msg[i]->msg)) == NULL)
351                return -1;
352
353            if (list_append(pam_msgs, (void *)str) == NULL) {
354                syslog(LOG_ERR, "list_append failed: %s\n", str);
355                free(str);
356                return -1;
357            }
358
359            last_pam_msg = str;
360            break;
361        }
362        case PAM_BINARY_PROMPT:
363        {
364            /* More or less ripped from PAM's misc_conv.c */
365
366            /* As of this revision, PAM 0.77 functionality of
367             * PAM_BINARY_PROMPT was still under development.  This
368             * code is placed here to hopefully be compatible with any
369             * existing pam modules that may use PAM_BINARY_PROMPT.
370             */
371
372            pamc_bp_t binary_prompt = NULL;
373
374            if (!msg[i]->msg || !pam_binary_handler_fn)
375                goto error;
376
377            PAM_BP_RENEW(&binary_prompt,
378                         PAM_BP_RCONTROL(msg[i]->msg),
379                         PAM_BP_LENGTH(msg[i]->msg));
380            PAM_BP_FILL(binary_prompt, 0, PAM_BP_LENGTH(msg[i]->msg),
381                        PAM_BP_RDATA(msg[i]->msg));
382
383            if (pam_binary_handler_fn(appdata_ptr,
384                                      &binary_prompt) != PAM_SUCCESS
385                || (binary_prompt == NULL))
386                goto error;
387
388            string = (char *) binary_prompt;
389            binary_prompt = NULL;
390            
391            break;
392        }
393        case PAM_PROMPT_ECHO_OFF:
394        case PAM_PROMPT_ECHO_ON:
395            /* Giving the user a prompt for input defeats the purpose
396             * of not sending the data over stdout.  A pam module
397             * under mqsh should never ask the user for data.  So fall
398             * through.
399             */
400        default:
401            syslog(LOG_ERR, "bad conversation: %d", msg[i]->msg_style);
402            goto error;
403            break;
404        }
405
406        /* Must set values, or _pam_drop_reply in pam modules will fail */
407        reply[i].resp_retcode = 0;
408        if (string != NULL) {
409            reply[i].resp = string;
410            string = NULL;
411        }
412        else
413            reply[i].resp = NULL;
414    }
415    
416    *resp = reply;
417    reply = NULL;
418
419    return PAM_SUCCESS;
420
421 error:
422    if (reply) {
423        for (i = 0; i < num_msg; i++) {
424            if (reply[i].resp == NULL)
425                continue;
426            if (msg[i]->msg_style == PAM_BINARY_PROMPT)
427                pam_binary_handler_free(appdata_ptr,
428                                        (pamc_bp_t *) &reply[i].resp);
429            else
430                /* Uhh, we shouldn't be able to get here */
431                free(reply[i].resp);
432            reply[i].resp = NULL;
433        }
434        free(reply);
435        reply = NULL;
436    }
437    return PAM_CONV_ERR;
438}
439
440int pamauth(struct passwd *pwd, char *service, char *remuser,
441            char *hostname, char *locuser) {
442    static struct pam_conv conv;
443    int retcode;
444    List pam_msgs = NULL;
445
446    if ((pam_msgs = list_create((ListDelF)free)) == NULL) {
447        syslog(LOG_ERR, "list_create() failed\n");
448        pam_errmsg = "Internal System Error";
449        goto error;
450    }
451
452    conv.conv = qshell_conv;
453    conv.appdata_ptr = (void *)&pam_msgs;
454
455    retcode = pam_start(service, locuser, &conv, &pamh);
456    if (retcode != PAM_SUCCESS) { 
457        syslog(LOG_ERR, "pam_start: %s\n", pam_strerror(pamh, retcode));
458        goto error;
459    }
460    pam_set_item(pamh, PAM_RUSER, remuser);
461    pam_set_item(pamh, PAM_RHOST, hostname);
462    pam_set_item(pamh, PAM_TTY, service);
463
464    retcode = pam_authenticate(pamh, 0);
465    if (retcode == PAM_SUCCESS) {
466        last_pam_msg = NULL;
467        retcode = pam_acct_mgmt(pamh, 0);
468    }
469
470    if (retcode == PAM_SUCCESS) {
471        /*
472         * Why do we need to set groups here?
473         * Also, this stuff should be moved down near where the setuid() is.
474         */
475        if (setgid(pwd->pw_gid) != 0) {
476            pam_end(pamh, PAM_SYSTEM_ERR);
477            goto error;
478        }
479
480        if (initgroups(locuser, pwd->pw_gid) != 0) {
481            pam_end(pamh, PAM_SYSTEM_ERR);
482            goto error;
483        }
484        last_pam_msg = NULL;
485        retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
486    }
487
488    if (retcode == PAM_SUCCESS) {
489        last_pam_msg = NULL;
490        retcode = pam_open_session(pamh, 0);
491    }
492    if (retcode != PAM_SUCCESS) {
493        pam_end(pamh, retcode);
494        if (last_pam_msg != NULL) {
495            /* Dump all pam messages to syslog, Send only the
496             * last message to the user
497             */
498            ListIterator itr = list_iterator_create(pam_msgs);
499            char *msg;
500            while (msg = (char *)list_next(itr))
501                syslog(LOG_ERR, "pam_msg: %s\n", msg);
502            list_iterator_destroy(itr);
503            snprintf(pam_errmsgbuf, PAM_ERRMSGLEN, "%s", last_pam_msg);
504            pam_errmsg = pam_errmsgbuf;
505            return -1;    
506        }
507        goto error;
508    }
509    return 0;
510
511 error:
512    if (pam_msgs)
513        list_destroy(pam_msgs);
514    syslog(LOG_ERR, "PAM Authentication Failure\n");
515    pam_errmsg = "Permission Denied";
516    return -1;
517}
518#endif /* USE_PAM */
519
520
521#define REMOTE_PREFIX "QSHELL_REMOTE_"
522
523/*
524 * Return nonzero if environment variable 'var' is a qshell "wrapped"
525 *  environment variable, i.e. one that should be reset in the environment
526 *  without the REMOTE_PREFIX
527 */
528static int _is_wrapped_qshell_env_var (const char *var)
529{
530    const char * valid_vars[] = { "LD_LIBRARY_PATH", "LD_PREOPEN", NULL };
531    const char *p, *q;
532
533    if (strncmp (var, REMOTE_PREFIX, strlen (REMOTE_PREFIX)) != 0)
534        return (0);
535
536    p = var + strlen (REMOTE_PREFIX);
537
538    for (q = valid_vars[0]; q != NULL;  q++) {
539        if (strncmp (p, q, strlen (q)) == 0)
540            return (1);
541    }
542
543    return (0);
544}
545
546/*
547 *  Read Qshell arguments from remote side (via stdin)
548 *
549 *  Returns completed Elan capability struct and qsw_info in
550 *   `cap' and `qinfo' on success. Returns -1 on failure.
551 *
552 */
553int get_qshell_info(ELAN_CAPABILITY *cap, qsw_info_t *qinfo, 
554                    char *cwd, int cwdlen) {
555    int i, envcount;
556    char envstr[4096];
557    char tmpstr[1024];
558
559    /* read cwd of client - will change to cwd on remote side */
560    if (getstr(cwd, cwdlen, "cwd") < 0)
561        return -1;
562
563    /* read environment of client - will replicate on remote side */
564    if (getstr(tmpstr, sizeof(tmpstr), "envcount") < 0)
565        return -1;
566
567    envcount = atoi(tmpstr);
568    while (envcount-- > 0) {
569        if (getstr(envstr, sizeof(envstr), "envstr") < 0)
570            return -1;
571
572        /* Following is a mem-leak on some systems, on others it is
573         * the proper way to call putenv.
574         */
575
576        /*
577         * For variables with REMOTE_PREFIX, set the real environment
578         *  variable (e.g. LD_LIBRARY_PATH)
579         */
580        if (_is_wrapped_qshell_env_var (envstr)) 
581            putenv(strdup(envstr + strlen(REMOTE_PREFIX)));
582        else 
583            putenv(strdup(envstr));
584    }
585
586    /* read elan capability */
587    if (getstr(tmpstr, sizeof(tmpstr), "capability") < 0)
588        return -1;
589
590    if (qsw_decode_cap(tmpstr, cap) < 0) {
591        errlog("error reading capability: %s\n", tmpstr);
592        return -1;
593    }
594
595    for (i = 0; i < qsw_cap_bitmap_count(); i += 16) {
596        if (getstr(tmpstr, sizeof(tmpstr), "capability") < 0)
597            return -1;
598
599        if (qsw_decode_cap_bitmap(tmpstr, cap, i) < 0) {
600            errlog("error reading capability bitmap(%d): %s\n", i, tmpstr);
601            return -1;
602        }
603    }
604
605    /* read info structure */
606    if (getstr(tmpstr, sizeof(tmpstr), "qsw info") < 0)
607        return -1;
608
609    if (qsw_decode_info(tmpstr, qinfo) < 0) {
610        errlog("error reading qsw info: %s\n", tmpstr);
611        return -1;
612    }
613
614    return 0;
615}
616
617void stderr_parent(int sock, int pype, int prgid) {
618    fd_set ready, readfrom;
619    char buf[BUFSIZ], sig;
620    int one = 1;
621    int nfd, cc, guys = 2;
622
623    ioctl(pype, FIONBIO, (char *) &one);
624    /* should set s nbio! */
625
626    FD_ZERO(&readfrom);
627    FD_SET(sock, &readfrom);
628    FD_SET(pype, &readfrom);
629    nfd = (pype > sock) ? pype + 1 : sock + 1; 
630
631    while (guys > 0) {
632        ready = readfrom;
633        if (select(nfd, &ready, NULL, NULL, NULL) < 0) {
634            if (errno != EINTR)
635                break;
636            continue;
637        }
638        if (FD_ISSET(sock, &ready)) {
639            if (read(sock, &sig, 1) <= 0) {
640                FD_CLR(sock, &readfrom);
641                guys--;
642                if (guys == 1) 
643                    /* pdsh client has crashed, kill the job */
644                    qsw_prgsignal(prgid, SIGKILL);
645            }
646            else
647                qsw_prgsignal(prgid, sig);
648        }
649        if (FD_ISSET(pype, &ready)) {
650            if ((cc = read(pype, buf, sizeof(buf))) <= 0) {
651                shutdown(sock, 2);
652                FD_CLR(pype, &readfrom);
653                guys--;
654            } else {
655                if (guys == 2)
656                    write(sock, buf, cc);
657            }
658        }
659    }
660
661#ifdef USE_PAM
662    /* Must close here, b/c other process runs exec() */
663    pam_close_session(pamh, 0);
664    pam_end(pamh, PAM_SUCCESS);
665#endif
666    exit(0);
667}
668
669
670static int _qshell_init(void) {
671    int port;
672
673    signal(SIGINT, SIG_DFL);
674    signal(SIGQUIT, SIG_DFL);
675    signal(SIGTERM, SIG_DFL);
676
677    /* 
678     * Remote side sends us a stringified port number to send back
679     * stderr on.  If zero, stderr is folded in with stdout.
680     */
681    alarm(60);
682    if ((port = getint()) < 0) {
683        syslog(LOG_ERR, "can't read port: %m");
684        exit(1);
685    }
686    alarm(0);
687
688    return port;
689}
690
691
692/*
693 *  Execute the Elan MPI job given the qshell args in `args'
694 */
695static void _qshell_execute(struct qshell_args *args)
696{
697    int ifd, pid, pv[2];
698    ELAN_CAPABILITY cap;
699    qsw_info_t qinfo;
700    char *theshell, *shellname;
701    char cwd[MAXPATHLEN+1];
702
703    if (get_qshell_info(&cap, &qinfo, cwd, sizeof(cwd)) < 0)
704        exit(1);
705
706    /* A single \0 written back on the socket indicates success.  Error
707     * would be indicated by a nonzero code followed by error string,
708     * e.g. through calling error().
709     */
710    if (write(2, "\0", 1) != 1) {
711        errlog("%s: bad write of null to stdout: %s\n", 
712                typename, strerror(errno));
713        exit(1);
714    }
715    sent_null = 1;
716
717    /*
718     * Fork off a process to send back stderr if requested.
719     */
720    if (args->port) {
721        if (pipe(pv) < 0) {
722            error("Can't make pipe.\n");
723            exit(1);
724        }
725        if ((pid = fork()) == -1) {
726            error("Can't fork; try again.\n");
727            exit(1);
728        }
729        if (pid) {
730            close(0);
731            close(1);
732            close(2);
733            close(pv[1]);
734            stderr_parent(args->sock, pv[0], qinfo.prgnum);
735            /* NOTREACHED */
736        }
737        setpgrp();
738        close(args->sock);
739        close(pv[0]);
740        dup2(pv[1], 2);
741        close(pv[1]);
742    }
743
744    /*
745     * Set up quadrics Elan capabilities and program desc.
746     * Fork a couple of times in here.
747     * On error, send diagnostics to stderr and exit.
748     */
749    qsw_setup_program(&cap, &qinfo, args->pwd->pw_uid);
750
751    /*
752     *  Become the locuser, etc. etc. then exec the shell command.
753     */
754
755    /* set the path to the shell */
756    theshell = args->pwd->pw_shell;
757    if (!theshell || !*theshell)
758        theshell = _PATH_BSHELL;
759
760#if BSD > 43
761    if (setlogin(args->pwd->pw_name) < 0)
762        errlog("setlogin() failed: %s\n", strerror(errno));
763#endif
764
765#ifndef USE_PAM
766    /* if PAM, already done */
767    if (setgid(args->pwd->pw_gid)) {
768        errlog("setgid: %s\n", strerror(errno));
769        exit(1);
770    }
771    if (initgroups(args->pwd->pw_name, args->pwd->pw_gid)) {
772        errlog("initgroups: %s\n", strerror(errno));
773        exit(1);
774    }
775#endif
776
777    if (setuid(args->pwd->pw_uid)) {
778        errlog("setuid: %s\n", strerror(errno));
779        exit(1);
780    }
781
782    strncat(homedir, args->pwd->pw_dir, sizeof(homedir) - 6);
783    homedir[sizeof(homedir) - 1] = 0;
784
785    strcat(path, _PATH_DEFPATH);
786
787    strncat(shell, theshell, sizeof(shell) - 7);
788    shell[sizeof(shell) - 1] = 0;
789
790    strncat(username, args->pwd->pw_name, sizeof(username) - 6);
791    username[sizeof(username) - 1] = 0;
792
793    if ((shellname = strrchr(theshell, '/')) == NULL)
794        shellname = theshell;
795    else
796        shellname++;
797
798    if (paranoid) {
799        syslog(LOG_INFO | LOG_AUTH, "%s@%s as %s: cmd='%s' cwd='%s'",
800                (args->remuser == NULL) ? args->pwd->pw_name : args->remuser, 
801                args->hostname, 
802                (args->locuser == NULL) ? args->pwd->pw_name : args->locuser, 
803                args->cmdbuf, cwd);
804    }
805
806    endpwent();
807
808    /* override USER, HOME, SHELL environment vars */
809    putenv(username);
810    putenv(homedir);
811    putenv(shell);
812
813    /* change to client working dir */
814    if (chdir(cwd) < 0) {
815        errlog("chdir to client working directory: %s\n", strerror(errno));
816        exit(1);
817    }
818
819    /*
820     * Close all fds, in case libc has left fun stuff like
821     * /etc/shadow open.
822     */
823#define FDS_INUSE 2
824    for (ifd = getdtablesize() - 1; ifd > FDS_INUSE; ifd--)
825        close(ifd);
826
827    execl(theshell, shellname, "-c", args->cmdbuf, 0);
828    errlog("failed to exec shell: %s\n", strerror(errno));
829    exit(1);
830}
831
832void network_init(int fd, struct sockaddr_in *fromp, int check_port) {
833    struct linger linger;
834    socklen_t fromlen;
835    int on = 1;
836    int port;
837
838    fromlen = sizeof(*fromp);
839    if (getpeername(fd, (struct sockaddr *) fromp, &fromlen) < 0) {
840        syslog(LOG_ERR, "getpeername: %m");
841        _exit(1);
842    }
843    if (keepalive &&
844        setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0)
845        syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
846    linger.l_onoff = 1;
847    linger.l_linger = 60;       /* XXX */
848    if (setsockopt(fd, SOL_SOCKET, SO_LINGER, 
849                   (char *) &linger, sizeof(linger)) < 0)
850        syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
851
852    if (fromp->sin_family != AF_INET) {
853        syslog(LOG_ERR, "malformed \"from\" address (af %d)\n", fromp->sin_family);
854        exit(1);
855    }
856
857#ifdef IP_OPTIONS
858    {
859        u_char optbuf[BUFSIZ / 3], *cp;
860        char lbuf[BUFSIZ + 1], *lp;
861        socklen_t optsize = sizeof(optbuf);
862        int ipproto;
863        struct protoent *ip;
864
865        if ((ip = getprotobyname("ip")) != NULL)
866            ipproto = ip->p_proto;
867        else
868            ipproto = IPPROTO_IP;
869        if (!getsockopt(0, ipproto, IP_OPTIONS, (char *) optbuf, &optsize) && 
870                optsize != 0) {
871            lp = lbuf;
872
873            /*
874             * If these are true, this will not run off the end of lbuf[].
875             */
876            assert(optsize <= BUFSIZ / 3);
877            assert(3 * optsize <= BUFSIZ);
878            for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3)
879                snprintf(lp, 4, " %2.2x", *cp);
880
881            syslog(LOG_NOTICE, "Connection received from %s using IP options"
882                  " (ignored): %s", inet_ntoa(fromp->sin_addr), lbuf);
883
884            if (setsockopt(0, ipproto, IP_OPTIONS, NULL, optsize) != 0) {
885                syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m");
886                exit(1);
887            }
888        }
889    }
890#endif
891
892    /*
893     * Check originating port for validity.
894     */
895    if (check_port) {
896        port = ntohs(fromp->sin_port);
897        if (port >= IPPORT_RESERVED || port < IPPORT_RESERVED / 2) {
898            syslog(LOG_NOTICE | LOG_AUTH, "Connection from %s on illegal port",
899                    inet_ntoa(fromp->sin_addr));
900            exit(1);
901        }
902    }
903}
904
905
906int qshell(int argc, char *argv[], QshGetArgsF getargs, char *name, 
907           int check_port) {
908    int ch;
909    struct sockaddr_in from;
910    struct qshell_args args;
911    _check_rhosts_file=1;
912
913    strncpy(typename, name, BUFSIZ);
914    openlog(typename, LOG_PID | LOG_ODELAY, LOG_DAEMON);
915    err_init(xbasename(argv[0]));
916
917    opterr = 0;
918    while ((ch = getopt(argc, argv, OPTIONS)) != EOF) {
919        switch (ch) {
920            case 'a':
921                check_all = 1;
922                break;
923
924            case 'h':
925                allow_root_rhosts = 1;
926                break;
927
928            case 'l':
929                _check_rhosts_file = 0;
930                break;
931
932            case 'n':
933                keepalive = 0;
934                break;
935
936            case 'L':
937                paranoid = 1;
938                break;
939
940            case '?':
941            default:
942                syslog(LOG_ERR, "usage: %s [-%s]", typename, OPTIONS);
943                exit(2);
944        }
945    }
946    argc -= optind;
947    argv += optind;
948
949    network_init(0, &from, check_port);
950
951    if (qsw_init() < 0) {
952        syslog(LOG_ERR, "qsw_init failed. Exiting...");
953        exit(1);
954    }
955
956    qsw_spawn_neterr_thr();
957
958    args.port = _qshell_init();
959    getargs(&from, &args);
960    _qshell_execute(&args);
961
962    qsw_fini();
963
964    return 0;
965}
966
967/*
968 * vi: tabstop=4 shiftwidth=4 expandtab
969 */