PageRenderTime 30ms CodeModel.GetById 13ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 0ms

/src/modules/k4cmd.c

https://code.google.com/
C | 384 lines | 253 code | 35 blank | 96 comment | 54 complexity | 969207fb554b78ab5a392ef928859098 MD5 | raw file
  1/*****************************************************************************\
  2 *  $Id$
  3 *****************************************************************************
  4 *  Copyright (C) 2001-2002 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 * This code is based on the MIT Kerberos IV kcmd.c with some athena hacks 
 29 * removed and MT safety added, and the interface changed.  Original UC regents
 30 * header from BSD included below.
 31 *
 32 * XXX for some reason it takes a really long time (several seconds per node)
 33 * to acquire service tickets!  Why?
 34 */
 35
 36/*
 37 * Copyright (c) 1983 Regents of the University of California. All rights
 38 * reserved. 
 39 *
 40 * Redistribution and use in source and binary forms are permitted provided that
 41 * the above copyright notice and this paragraph are duplicated in all such
 42 * forms and that any documentation, advertising materials, and other
 43 * materials related to such distribution and use acknowledge that the
 44 * software was developed by the University of California, Berkeley.  The
 45 * name of the University may not be used to endorse or promote products
 46 * derived from this software without specific prior written permission. THIS
 47 * SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 48 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 49 * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
 50 */
 51
 52#if     HAVE_CONFIG_H
 53#include "config.h"
 54#endif
 55
 56#if	HAVE_PTHREAD_H
 57#include <pthread.h>
 58#endif
 59
 60#include <stdio.h>
 61#include <string.h>
 62#include <sys/types.h>
 63#include <ctype.h>
 64#include <pwd.h>
 65#include <sys/param.h>
 66#if HAVE_SYS_FILE_H
 67#include <sys/file.h>
 68#endif
 69#include <signal.h>
 70#include <sys/socket.h>
 71#include <sys/stat.h>
 72
 73#include <netinet/in.h>
 74
 75#include <netdb.h>
 76#include <errno.h>
 77#include <krb.h>
 78#include <kparse.h>
 79#include <fcntl.h>              /* for F_SETOWN */
 80
 81#include "src/common/xmalloc.h"
 82#include "src/common/xstring.h"
 83#include "src/common/macros.h"
 84#include "src/common/err.h"
 85#include "src/common/list.h"
 86#include "src/common/xpoll.h"
 87#include "src/pdsh/privsep.h"
 88
 89#ifndef MAXHOSTNAMELEN
 90#define MAXHOSTNAMELEN 64
 91#endif
 92
 93#define KCMD_PORT 544
 94
 95#if STATIC_MODULES
 96#  define pdsh_module_info k4cmd_module_info
 97#  define pdsh_module_priority k4cmd_module_priority
 98#endif    
 99
100int pdsh_module_priority = DEFAULT_MODULE_PRIORITY;
101
102extern errno;
103extern char *inet_ntoa();
104
105static int k4cmd_init(opt_t *);
106static int k4cmd_signal(int, void *, int);
107static int k4cmd(char *, char *, char *, char *, char *, int, int *, void **); 
108
109/* 
110 * Export pdsh module operations structure
111 */
112struct pdsh_module_operations k4cmd_module_ops = {
113    (ModInitF)       NULL, 
114    (ModExitF)       NULL, 
115    (ModReadWcollF)  NULL, 
116    (ModPostOpF)     NULL,
117};
118
119/*
120 *  Export rcmd module operations
121 */
122struct pdsh_rcmd_operations k4cmd_rcmd_ops = {
123    (RcmdInitF)  k4cmd_init,
124    (RcmdSigF)   k4cmd_signal,
125    (RcmdF)      k4cmd,
126};
127
128/* 
129 * Export module options
130 */
131struct pdsh_module_option k4cmd_module_options[] = 
132 { 
133   PDSH_OPT_TABLE_END
134 };
135
136/* 
137 * k4cmd module info 
138 */
139struct pdsh_module pdsh_module_info = {
140    "rcmd",
141    "k4",
142    "Jim Garlick <garlick@llnl.gov>",
143    "kerberos based rcmd connect method",
144    DSH | PCP, 
145
146    &k4cmd_module_ops,
147    &k4cmd_rcmd_ops,
148    &k4cmd_module_options[0],
149};
150
151static int k4cmd_init(opt_t * opt)
152{
153    /* not implemented */
154    return 0;
155}
156
157/*
158 * Use rcmd backchannel to propagate signals.
159 *      efd (IN)        file descriptor connected socket (-1 if not used)
160 *      signum (IN)     signal number to send
161 */
162static int k4cmd_signal(int efd, void *arg, int signum)
163{
164    char c;
165
166    if (efd >= 0) {
167        /* set non-blocking mode for write - just take our best shot */
168        if (fcntl(efd, F_SETFL, O_NONBLOCK) < 0)
169            err("%p: fcntl: %m\n");
170        c = (char) signum;
171        write(efd, &c, 1);
172    }
173    return 0;
174}
175
176/*
177 * The rcmd call itself.
178 *      ahost (IN)      remote hostname
179 *	addr (IN)	IP address
180 *      locuser (IN)    local username
181 *      remuser (IN)    remote username
182 *      cmd (IN)        command to execute
183 *       rank (IN)	MPI rank
184 *      fd2p (IN/OUT)   if non-NULL, open stderr backchannel on this fd
185 *      s (RETURN)      socket for stdout/sdin or -1 on failure
186 */
187static int
188k4cmd(char *ahost, char *addr, char *locuser, char *remuser, char *cmd,
189      int rank, int *fd2p, void **arg)
190{
191    KTEXT_ST ticket;            /* kerberos IV context */
192    CREDENTIALS cred;
193    Key_schedule schedule;
194    MSG_DAT msg_data;
195    struct sockaddr_in faddr;
196    struct sockaddr_in laddr;
197
198    int s, pid;
199    sigset_t oldset, blockme;
200    struct sockaddr_in sin, from;
201    char c;
202    int lport = IPPORT_RESERVED - 1;
203    unsigned long krb_options = 0L;
204    static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
205    int status;
206    int rc, rv;
207    struct xpollfd xpfds[2];
208
209    pid = getpid();
210
211    sigemptyset(&blockme);
212    sigaddset(&blockme, SIGURG);
213    pthread_sigmask(SIG_BLOCK, &blockme, &oldset);
214    for (;;) {
215        s = privsep_rresvport(&lport);
216        if (s < 0) {
217            if (errno == EAGAIN)
218                err("%p: %S: socket: All ports in use\n", ahost);
219            else
220                err("%p: %S: k4cmd: socket: %m\n", ahost);
221            pthread_sigmask(SIG_SETMASK, &oldset, NULL);
222            return (-1);
223        }
224        fcntl(s, F_SETOWN, pid);
225        sin.sin_family = AF_INET;
226        memcpy((caddr_t) & sin.sin_addr, addr, IP_ADDR_LEN);
227        sin.sin_port = htons(KCMD_PORT);
228
229        rv = connect(s, (struct sockaddr *) &sin, sizeof(sin));
230
231        if (rv >= 0)
232            break;
233        (void) close(s);
234        if (errno == EADDRINUSE) {
235            lport--;
236            continue;
237        }
238        if (errno == EINTR)
239            err("%p: %S: connect timed out\n", ahost);
240        else
241            err("%p: %S: %m\n", ahost);
242        pthread_sigmask(SIG_SETMASK, &oldset, NULL);
243        return (-1);
244    }
245    if (fd2p == 0) {
246        write(s, "", 1);
247        lport = 0;
248    } else {
249        char num[8];
250        int s2, s3;
251        socklen_t len = sizeof(from);
252
253        s2 = privsep_rresvport(&lport);
254        if (s2 < 0) {
255            goto bad;
256        }
257        listen(s2, 1);
258        (void) snprintf(num, sizeof(num), "%d", lport);
259        if (write(s, num, strlen(num) + 1) != strlen(num) + 1) {
260            err("%p: %S: write: setting up stderr: %m\n", ahost);
261            (void) close(s2);
262            goto bad;
263        }
264        errno = 0;
265        xpfds[0].fd = s;
266        xpfds[1].fd = s2;
267        xpfds[0].events = xpfds[1].events = XPOLLREAD;
268        if (((rv = xpoll(xpfds, 2, -1)) < 0) || rv != 1 || (xpfds[0].revents > 0)) {
269          if (errno != 0)
270            err("%p: %S: k4cmd: xpoll (setting up stderr): %m\n", ahost);
271          else
272            err("%p: %S: k4cmd: xpoll: protocol failure in circuit setup\n", ahost);
273          (void) close(s2);
274          goto bad;
275        }
276        s3 = accept(s2, (struct sockaddr *) &from, &len);
277        (void) close(s2);
278        if (s3 < 0) {
279            err("%p: %S: accept: %m\n", ahost);
280            lport = 0;
281            goto bad;
282        }
283        *fd2p = s3;
284        from.sin_port = ntohs((u_short) from.sin_port);
285    }
286
287    /*
288     * Kerberos-authenticated service.  Don't have to send locuser, since
289     * its already in the ticket, and we'll extract it on the other side. 
290     */
291    /*krb_options |= KOPT_DONT_CANON; */
292    pthread_mutex_lock(&mylock);
293    status = krb_sendauth(krb_options, s, &ticket, "rcmd", ahost,
294                          NULL, (unsigned long) pid, &msg_data,
295                          &cred, schedule, &laddr, &faddr, "KCMDV0.1");
296
297    if (status != KSUCCESS) {
298        /*
299         * this part involves some very intimate knowledge of a
300         * particular sendauth implementation to pry out the old
301         * bits. This only catches the case of total failure -- but
302         * that's the one where we get useful data from the remote
303         * end. If we even get an authenticator back, then the
304         * problem gets diagnosed locally anyhow. 
305         */
306        extern KRB_INT32 __krb_sendauth_hidden_tkt_len;
307        char *old_data = (char *) &__krb_sendauth_hidden_tkt_len;
308        char tmpbuf[LINEBUFSIZE];
309        char *p = tmpbuf;
310
311        if ((status == KFAILURE) && (*old_data == 1)) {
312            strncpy(tmpbuf, old_data + 1, 3);
313            tmpbuf[3] = '\0';
314            err("%p: %S: %s", ahost, tmpbuf);
315            *old_data = (-1);
316        }
317        if ((status == KFAILURE) && (*old_data == (char) -1)) {
318            while (read(s, &c, 1) == 1) {
319                /*(void) write(2, &c, 1); */
320                *p++ = c;
321                if (c == '\n')
322                    break;
323            }
324            *p++ = '\0';
325            err("%p: %S: %s", ahost, tmpbuf);
326            status = -1;
327        }
328        switch (status) {
329        case KDC_PR_UNKNOWN:
330            err("%p: %S: not registered for kerberos\n", ahost);
331            break;
332        case NO_TKT_FIL:
333            err("%p: %S: no tickets file found\n", ahost);
334            break;
335        default:
336            err("%p: %S: k4cmd failed: %s\n", ahost,
337                (status == -1) ? "k4cmd protocol failure" :
338                krb_get_err_text(status));
339        }
340        pthread_mutex_unlock(&mylock);
341        goto bad2;
342    }
343    pthread_mutex_unlock(&mylock);
344    (void) write(s, remuser, strlen(remuser) + 1);
345    (void) write(s, cmd, strlen(cmd) + 1);
346
347    if ((rc = read(s, &c, 1)) != 1) {
348        if (rc == -1) {
349            err("%p: %S: read: %m\n", ahost);
350        } else {
351            err("%p: %S: k4cmd: bad connection with remote host\n", ahost);
352        }
353        goto bad2;
354    }
355    if (c != 0) {
356        /* retrieve error string from remote server */
357        char tmpbuf[LINEBUFSIZE];
358        char *p = tmpbuf;
359
360        while (read(s, &c, 1) == 1) {
361            *p++ = c;
362            if (c == '\n')
363                break;
364        }
365        if (c != '\n')
366            *p++ = '\n';
367        *p++ = '\0';
368        err("%S: %s", ahost, tmpbuf);
369        goto bad2;
370    }
371    pthread_sigmask(SIG_SETMASK, &oldset, NULL);
372    return (s);
373  bad2:
374    if (lport)
375        (void) close(*fd2p);
376  bad:
377    (void) close(s);
378    pthread_sigmask(SIG_SETMASK, &oldset, NULL);
379    return (-1);
380}
381
382/*
383 * vi:tabstop=4 shiftwidth=4 expandtab
384 */