src/vsl_fifo.c

Go to the documentation of this file.
00001 /*
00002  * Copyright 2006, Internet2
00003  * Legal conditions are in file LICENSE
00004  * (MD5 = c434f2e53b8089d8b4d0172c7ce07360).
00005  */
00006 
00007 // Don't document
00008 #ifdef ENABLE_FIFO
00009 
00010 /**
00011  * @file   vsl_fifo.c
00012  * @author Nikolaus Rath
00013  * @brief  SSH handshake functions for the security API
00014  *
00015  **/
00016 
00017 #include "vsl_api.h"
00018 #include "vsl_util.h"
00019 #include "vsl_fifo.h"
00020 #include "vsl.h"
00021 
00022 #include <string.h>
00023 #include <errno.h>
00024 #include <unistd.h>
00025 #include <fcntl.h>
00026 #include <sys/types.h>
00027 #include <sys/wait.h>
00028 #include <time.h>
00029 #include <sys/sem.h>
00030 #include <sys/mman.h>
00031 #include <stdio.h>
00032 #include <sys/ipc.h>
00033 
00034 
00035 //! Queue for received FIFO messages
00036 /**
00037  * If several VSL sockets operate with the same FIFO, it is
00038  * possible that a call to recv_key_ssh() reads a key for a
00039  * different client. In this case, the key is stored in this
00040  * buffer.
00041  */
00042 static struct {
00043     uint32_t clid;
00044     char key[VSL_KEYLENGTH];
00045     time_t timestamp;
00046 } *msgqueue;
00047 
00048 //! Next unused client id (free to overflow)
00049 static uint32_t *client_id;
00050 
00051 //! Semaphore set to synchronize msgqueue, FIFO and client_id access
00052 static int sem = -1;
00053 
00054 /**
00055  * init function for ssh part, to be called by vsl_init().
00056  *
00057  * \return
00058  * - 0 on success
00059  * - #VSL_ERRNO if a system call failed (errno contains the precise
00060  *   error)
00061  **/
00062 int ssh_init(void) {
00063 
00064     /* Acquire semaphore set
00065      * Semaphore 0 is for FIFO and msgqueue, semaphore 1
00066      * for client_id */
00067     if ( (sem = semget(IPC_PRIVATE, 2, 0600 )) == -1 )
00068         LOGFAIL(VSL_ERRNO, "Cannot create semaphore: %s!", strerror(errno));
00069     if ( semctl(sem, 0, SETVAL, 1) == -1 ||
00070          semctl(sem, 1, SETVAL, 1) == -1 )
00071         LOGFAIL(VSL_ERRNO, "Cannot init semaphore: %s!", strerror(errno));
00072 
00073 
00074     /* Acquire shared memory */
00075     if ( (msgqueue = mmap(0, sizeof(*msgqueue) * MSG_QUEUE_SIZE + sizeof(*client_id),
00076                           PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED,
00077                           -1, 0)) == MAP_FAILED )
00078         LOGFAIL(VSL_ERRNO, "Cannot create shm: %s!", strerror(errno));
00079 
00080     /* Initialize */
00081     memset( (void*) msgqueue, 0, sizeof(*msgqueue) * MSG_QUEUE_SIZE);
00082     client_id = (void*) msgqueue + sizeof(*msgqueue) * MSG_QUEUE_SIZE;
00083     *client_id = 0;
00084 
00085     LOG("Initialized ssh IPC.");
00086     return 0;
00087 }
00088 
00089 /**
00090  * destruct function for ssh part, to be called by vsl_destruct().
00091  * \return
00092  * - 0 on succes
00093  **/
00094 int ssh_destruct(void){
00095     /* Release semaphore */
00096     if ( semctl(sem, 0, IPC_RMID) == -1 )
00097         LOGFAIL(VSL_ERRNO, "Can't release semaphore: %s!", strerror(errno));
00098 
00099     /* Release SHM */
00100     if ( munmap(msgqueue, sizeof(*msgqueue) * MSG_QUEUE_SIZE + sizeof(*client_id)) != 0)
00101         LOGFAIL(VSL_ERRNO, "Can't release shm: %s!", strerror(errno));
00102 
00103     LOG("Destructed ssh IPC.");
00104     return 0;
00105 }
00106 
00107 
00108 /**
00109  * Sent ssh authentication request to notify the server that
00110  * a new handshake will be performed over the FIFO.
00111  *
00112  * @param[in] socket VSL socket
00113  * @return
00114  *   - 0 on success
00115  *   - #VFER_INPROGRESS socket is nonblocking and the call would block
00116  *   - #VFER_BADSOCK bad socket argument
00117  *   - #VFER_UNCONN socket is not connected
00118  *   - #VSL_ERRNO for all other errors (errno is set accordingly)
00119  **/
00120 int send_ssh_req(vsl_sock* socket) {
00121 
00122     LOG("attempting to send ssh auth request...");
00123 
00124     /* Create request structure */
00125     phead_t head;
00126     FILL_MAGIC(head.magic);
00127     head.type = P_SSH_REQ;
00128     head.datalen = htonl(0);
00129 
00130     /* Serialize structure */
00131     void *msg;
00132     int msglen;
00133     if ( (msg = write_objs(&msglen,
00134                            (void*) &head.magic, sizeof(head.magic),
00135                            (void*) &head.type, sizeof(head.type),
00136                            (void*) &head.datalen, sizeof(head.datalen),
00137                            NULL)) == NULL)
00138         LOGFAIL(VSL_ERRNO, "malloc failed: %s", strerror(errno));
00139 
00140 
00141     /* Sent packet */
00142     int ret = vfer_send(socket->vfd, msg, msglen);
00143     free(msg);
00144     if (ret == VFER_WOULDBLOCK)
00145         return VFER_INPROGRESS;
00146     else if (ret == VFER_BADSOCK ||
00147              ret == VFER_UNCONN)
00148         return ret;
00149     else if (ret > 0 &&
00150              ret != msglen) {
00151         LOG("cannot send fragmented auth request");
00152         abort();
00153     }
00154     else if (ret < 0) {
00155         LOG("unknown send error: %s", vfer_errortext(ret));
00156         abort();
00157     }
00158     socket->status = C_SSH_REQ_SENT;
00159 
00160     LOG("ssh auth request sent.");
00161     return(0);
00162 }
00163 
00164 
00165 /**
00166  * Receive ssh authentication response.
00167  *
00168  * The response contains the pathname for comparison and the client id
00169  * that has to be used when the shared secret is written into the
00170  * FIFO.
00171  *
00172  * @param[in] socket VSL socket to use
00173  * @param[in] path the pathname of the caller that is checked against
00174  * the received one.
00175  * @return
00176  *   - 0 on success
00177  *   - #VFER_INPROGRESS socket is nonblocking and the call would block
00178  *   - #VFER_BADSOCK bad socket argument
00179  *   - #VFER_UNCONN socket is not connected
00180  *   - #VSL_BADPROT protocol error
00181  *   - #VSL_BADPATH pathnames don't match
00182  *   - #VSL_ERRNO for all other errors (errno is set accordingly)
00183  **/
00184 int recv_ssh_res(vsl_sock* socket, const char* path) {
00185 
00186     LOG("attempting to read ssh auth response...");
00187 
00188     /* Receive packet */
00189     int ret;
00190     char msg[MAX_FRAME_SIZE];
00191     ret = vfer_recv(socket->vfd, (void*) msg, MAX_FRAME_SIZE);
00192     if (ret == VFER_WOULDBLOCK) {
00193         return VFER_INPROGRESS;
00194     }
00195     else if (ret == VFER_BADSOCK ||
00196              ret == VFER_UNCONN) {
00197         return ret;
00198     }
00199     else if (ret < 0) {
00200         LOG("unknown send error: %s", vfer_errortext(ret));
00201         abort();
00202     }
00203 
00204     /* Deserialize structure + extract client id */
00205     phead_t head;
00206     void *body = read_objs(msg,
00207                            (void*) &head.magic, sizeof(head.magic),
00208                            (void*) &head.type, sizeof(head.type),
00209                            (void*) &head.datalen, sizeof(head.datalen),
00210                            (void*) &(socket->client_id), sizeof(socket->client_id),
00211                            NULL);
00212     head.datalen = ntohl(head.datalen);
00213     socket->client_id = ntohl(socket->client_id);
00214 
00215     /* Verify packet */
00216     if(!CHECK_MAGIC(head.magic) ||
00217        head.type != P_SSH_RES)
00218         LOGFAIL(VSL_BADPROT, "invalid packet received");
00219 
00220     /* Extract pathname */
00221     if (head.datalen - sizeof(socket->client_id) > MAX_PATH)
00222         LOGFAIL(VSL_BADPROT, "received pathname too long");
00223 
00224     /* Compare paths */
00225     if ( strncmp(path, (char*)body, MAX_PATH) != 0 )
00226         LOGFAIL(VSL_BADPATH, "pathnames don't match.");
00227 
00228 
00229     LOG("ssh auth response read.");
00230 
00231     return(0);
00232 }
00233 
00234 
00235 /**
00236  * Fork ssh process to connect to remote host and write
00237  * into the specified FIFO.
00238  *
00239  * Stores pid and stdin of the ssh process in the socket.
00240  *
00241  * @param[in] sock VSL socket
00242  * @param[in] host hostname to connect to
00243  * @param[in] user username to use
00244  * @param[in] fifo pathname of fifo
00245  * @return
00246  *   - 0 on success
00247  *   - #VSL_ERRNO for all other errors (errno is set accordingly)
00248  **/
00249 int fork_ssh(vsl_sock* sock, const char* user,
00250              const char* host, const char* fifo) {
00251 
00252     LOG("attempting to fork ssh...");
00253 
00254     /* Create pipe */
00255     int fd[2];
00256     if ( pipe(fd) == -1 )
00257         LOGFAIL(VSL_ERRNO, "pipe failed: %s", strerror(err));
00258 
00259     /* Fork & Exec */
00260     pid_t pid;
00261     if ( (pid = fork()) == 0 ) {
00262         close(fd[1]);
00263         if ( fd[0] != STDIN_FILENO ) {
00264             if ( dup2(fd[0], STDIN_FILENO) != STDIN_FILENO )
00265                 LOGFAIL(VSL_ERRNO, "(child) dup2 failed: %s", strerror(err));
00266             close(fd[0]);
00267         }
00268 
00269         LOG("(child) calling 'ssh -T -l %s %s cat > %s'", user, host, fifo);
00270         execlp("ssh", "-T", "-l", user, host,
00271                "cat", ">", fifo, (char *) NULL);
00272         LOG("(child) exec failed: %s", strerror(errno));
00273         exit(255);
00274     }
00275     else if(pid == -1)
00276         LOGFAIL(VSL_ERRNO, "fork failed: %s", strerror(err));
00277 
00278     close(fd[0]);
00279     sock->ssh_pid = pid;
00280     sock->ssh_fd = fd[1];
00281 
00282     /* Set Nonblocking Flags */
00283     if (!is_blocking(sock)) {
00284         long fdflags;
00285         if ( (fdflags = fcntl(fd[1], F_GETFL, 0)) < 0 ||
00286              fcntl(fd[1], F_SETFL, fdflags | O_NONBLOCK) < 0 )
00287             LOGFAIL(VSL_ERRNO, "fcntl failed: %s", strerror(err));
00288     }
00289 
00290     LOG("ssh forked.");
00291     return(0);
00292 }
00293 
00294 
00295 /**
00296  * Sends the given key and client id over the ssh connection
00297  * to the server.
00298  *
00299  * @param[in] sock the socket to use
00300  * @param[in] key key to transfer
00301  * @return
00302  *   - 0 on success
00303  *   - #VFER_INPROGRESS if the socket is nonblocking and the call would block
00304  *   - #VSL_ERRNO for all other errors (errno is set accordingly)
00305  **/
00306 int send_key_ssh(vsl_sock* sock, const char* key) {
00307 
00308     LOG("attempting to send shared secret over ssh...");
00309     LOG("secret is " KEY_STR, KEY_VEC(key));
00310 
00311     /* Create header */
00312     phead_t head;
00313     FILL_MAGIC(head.magic);
00314     head.type = P_SSH_KEY;
00315     head.datalen = htonl(sizeof(uint32_t) + VSL_KEYLENGTH);
00316 
00317     /* Serialize packet */
00318     int msglen;
00319     void *msg;
00320     uint32_t clid = htonl(sock->client_id);
00321     if ( (msg = write_objs(&msglen,
00322                            (void*) &head.magic, sizeof(head.magic),
00323                            (void*) &head.type, sizeof(head.type),
00324                            (void*) &head.datalen, sizeof(head.datalen),
00325                            (void*) &clid, sizeof(clid),
00326                            (void*) key, VSL_KEYLENGTH,
00327                            NULL)) == NULL)
00328         LOGFAIL(VSL_ERRNO, "malloc failed: %s", strerror(err));
00329 
00330     /* Send key */
00331     if(write(sock->ssh_fd, msg, msglen) != msglen) {
00332         if(!is_blocking(sock) &&
00333            errno == EAGAIN)
00334             return(VFER_INPROGRESS);
00335         LOGFAIL(VSL_ERRNO, "write failed: %s", strerror(err));
00336     }
00337 
00338     /* Close fd */
00339     if ( close(sock->ssh_fd) != 0 )
00340         LOGFAIL(VSL_ERRNO, "close failed: %s", strerror(err));
00341 
00342     LOG("shared secret sent over ssh.");
00343 
00344     return(0);
00345 }
00346 
00347 
00348 /**
00349  * Close ssh connection and check exit status
00350  *
00351  * @param[in] sock VSL socket
00352  * @return
00353  *   - 0 on success
00354  *   - #VSL_SSHF ssh subprocess failed
00355  *   - #VSL_ERRNO for all other errors (errno is set accordingly)
00356  *   - #VFER_INPROGRESS socket is nonblocking and the call would block
00357  **/
00358 int close_ssh(vsl_sock* sock) {
00359     int ret, pidstat;
00360 
00361     LOG("attempting to close ssh connection...");
00362 
00363     /* Process still alive? */
00364     if(!is_blocking(sock)) {
00365         if( (ret=waitpid(sock->ssh_pid, &pidstat, WNOHANG)) == 0)
00366             return VFER_INPROGRESS;
00367     }
00368     else
00369         ret = waitpid(sock->ssh_pid, &pidstat, 0);
00370 
00371     if(ret != sock->ssh_pid)
00372         LOGFAIL(VSL_ERRNO, "waitpid failed: %s", strerror(err));
00373 
00374     /* Exit code? */
00375     if( !WIFEXITED(pidstat) )
00376         LOGFAIL(VSL_SSHF, "ssh process aborted.");
00377 
00378     if(WEXITSTATUS(pidstat) != 0)
00379         LOGFAIL(VSL_SSHF, "nonzero ssh exit status: %d", WEXITSTATUS(pidstat));
00380 
00381     LOG("ssh connection closed.");
00382     return(0);
00383 }
00384 
00385 
00386 
00387 /**
00388  * Receive an ssh authentication request
00389  *
00390  * @param[in] socket VSL socket
00391  *
00392  * @return:
00393  *   - 0 on success
00394  *   - #VFER_INPROGRESS socket is nonblocking and the call would block
00395  *   - #VFER_BADSOCK bad socket argument
00396  *   - #VFER_UNCONN socket is not connected
00397  *   - #VSL_BADPROT protocol error
00398  *   - #VSL_ERRNO for all other errors (errno is set accordingly)
00399  **/
00400 int recv_ssh_req(vsl_sock* socket) {
00401 
00402     LOG("attempting to receive ssh auth request...");
00403 
00404     /* Receive packet */
00405     char msg[MAX_FRAME_SIZE];
00406     int ret = vfer_recv(socket->vfd, msg, MAX_FRAME_SIZE);
00407     if (ret == VFER_WOULDBLOCK)
00408         return VFER_INPROGRESS;
00409     else if (ret == VFER_BADSOCK ||
00410              ret == VFER_UNCONN)
00411         return ret;
00412     else if (ret < 0) {
00413         LOG("unknown receive error: %s", vfer_errortext(ret));
00414         abort();
00415     }
00416 
00417     /* Deserialize structure */
00418     phead_t head;
00419     read_objs(msg,
00420               (void*) &head.magic, sizeof(head.magic),
00421               (void*) &head.type, sizeof(head.type),
00422               (void*) &head.datalen, sizeof(head.datalen),
00423               NULL);
00424 
00425     /* Verify packet */
00426     if(!CHECK_MAGIC(head.magic) ||
00427        head.type != P_SSH_REQ)
00428         LOGFAIL(VSL_BADPROT, "invalid packet received");
00429 
00430 
00431     // Assign client id
00432     if (semop(sem, (struct sembuf[]) { { 1, 1, SEM_UNDO }}, 1) == -1)
00433         LOGFAIL(VSL_ERRNO, "Cannot get lock: %s!", strerror(errno));
00434     socket->client_id = (*client_id)++;
00435 
00436     if ( semop(sem, (struct sembuf[]) {{1, -1, SEM_UNDO}}, 1) == -1)
00437         LOGFAIL(VSL_ERRNO, "Cannot release lock: %s!", strerror(errno));
00438 
00439     LOG("ssh auth request received.");
00440     return(0);
00441 }
00442 
00443 
00444 /**
00445  * Sent ssh authentication response to the client.
00446  *
00447  * @param[in] socket VSL socket
00448  * @param[in] path path of the fifo to use
00449  * @return
00450  *   - 0 on success
00451  *   - #VFER_INPROGRESS socket is nonblocking and the call would block
00452  *   - #VFER_BADSOCK bad socket argument
00453  *   - #VFER_UNCONN socket is not connected
00454  *   - #VSL_ERRNO for all other errors (errno is set accordingly)
00455  **/
00456 int send_ssh_res(vsl_sock* socket, const char* path) {
00457 
00458     LOG("attempting to send ssh auth response...");
00459 
00460     /* Create header */
00461     phead_t head;
00462     FILL_MAGIC(head.magic);
00463     head.type = P_SSH_RES;
00464     head.datalen =
00465         htonl(sizeof(uint32_t) + strlen(path) + 1); // pathlen + terminating \0
00466 
00467     /* Serialize packet */
00468     void *msg;
00469     int msglen;
00470     uint32_t clid = htonl(socket->client_id);
00471     if ( (msg = write_objs(&msglen,
00472                            (void*) &head.magic, sizeof(head.magic),
00473                            (void*) &head.type, sizeof(head.type),
00474                            (void*) &head.datalen, sizeof(head.datalen),
00475                            (void*) &clid, sizeof(socket->client_id),
00476                            (void*) path, strlen(path)+1,
00477                            NULL)) == NULL)
00478         LOGFAIL(VSL_ERRNO, "malloc failed: %s", strerror(err));
00479 
00480     /* Sent packet */
00481     int ret = vfer_send(socket->vfd, msg, msglen);
00482     free(msg);
00483     if (ret == VFER_WOULDBLOCK)
00484         return VFER_INPROGRESS;
00485     else if (ret == VFER_BADSOCK ||
00486              ret == VFER_UNCONN)
00487         return ret;
00488     else if (ret > 0 &&
00489              ret != msglen) {
00490         LOG("don't know how to handle fragmented sending!");
00491         abort();
00492     }
00493     else if (ret < 0) {
00494         LOG("unknown send error: %s!", vfer_errortext(ret));
00495         abort();
00496     }
00497     socket->status = C_SSH_REQ_SENT;
00498 
00499     LOG("ssh auth response sent.");
00500     return(0);
00501 }
00502 
00503 
00504 /**
00505  * Opens the FIFO for reading the shared secret.
00506  *
00507  * @param[in] sock VFD socket
00508  * @param[in] path path to the FIFO
00509  * @return
00510  *   - 0 on success
00511  *   - #VSL_ERRNO for all other errors (errno is set accordingly)
00512  **/
00513 int open_fifo(vsl_sock* sock, const char *path) {
00514     LOG("attempting to open FIFO %s...", path);
00515 
00516     int flags = O_RDONLY;
00517     if (!is_blocking(sock))
00518         flags |= O_NONBLOCK ;
00519 
00520     /** \todo Here we actually want to have the same semantics as in
00521      *  vfer_connect() / vfer_accept(): Is there a timeout if the
00522      *  socket is blocking?  */
00523     if ( (sock->ssh_fd = open(path, flags)) == -1 )
00524         LOGFAIL(VSL_ERRNO, "open failed: %s", strerror(err));
00525 
00526     LOG("opened new fd for FIFO %s.", path);
00527     return 0;
00528 }
00529 
00530 /**
00531  * Release the FIFO for the given socket and close it
00532  * if no other sockets need it.
00533  *
00534  * @param[in] sock the VSL socket
00535  * @return
00536  *   - 0 on success
00537  *   - #VSL_ERRNO for all other errors (errno is set accordingly)
00538  **/
00539 int close_fifo(vsl_sock* sock) {
00540     LOG("attempting to close FIFO...");
00541 
00542     if ( close(sock->ssh_fd) != 0 )
00543         LOGFAIL(VSL_ERRNO,"close failed: %s", strerror(err));
00544 
00545     LOG("FIFO closed.");
00546     return 0;
00547 }
00548 
00549 
00550 /**
00551  * Receive a key from the given FIFO
00552  *
00553  * @param[in] sock VSL socket
00554  * @return
00555  *   - 0 on success
00556  *   - #VFER_INPROGRESS socket is nonblocking and the call would block
00557  *   - #VSL_ERRNO for all other errors (errno is set accordingly)
00558  *   - #VSL_MAXCONN maximum number of open simulatienous fifo connections reached
00559  **/
00560 int recv_key_ssh(vsl_sock* sock) {
00561     LOG("attempting to get shared secret for client %d...", sock->client_id);
00562 
00563     /* Lookup whether we already read the secret
00564      * We don't need to lock because we only write if
00565      * the key belongs to us */
00566     for (int i=0; i < MSG_QUEUE_SIZE; i++)
00567         if (msgqueue[i].clid == sock->client_id &&
00568             msgqueue[i].timestamp != 0) {
00569             init_keys_s(sock, msgqueue[i].key);
00570             msgqueue[i].timestamp = 0;
00571             LOG("got key for client %d from cache.", sock->client_id);
00572             return 0;
00573         }
00574 
00575     /* Read until we find our secret */
00576     while(1) {
00577 
00578         LOG("attempting to read secret from FIFO...");
00579 
00580         /* Lock */
00581         if (semop(sem, (struct sembuf[]) {
00582             { 0, 1, SEM_UNDO | (is_blocking(sock) ? 0 : IPC_NOWAIT) }}, 1) == -1) {
00583             if (!is_blocking(sock) &&
00584                 errno == EAGAIN)
00585                 return VFER_INPROGRESS;
00586             LOGFAIL(VSL_ERRNO, "Cannot get lock: %s!", strerror(errno));
00587         }
00588 
00589 
00590         /* Look up in table again, maybe it's there now */
00591         for (int i=0; i < MSG_QUEUE_SIZE; i++)
00592             if (msgqueue[i].clid == sock->client_id &&
00593                 msgqueue[i].timestamp != 0) {
00594                 init_keys_s(sock, msgqueue[i].key);
00595                 msgqueue[i].timestamp = 0;
00596                 LOG("got key for client %d from cache.", sock->client_id);
00597                 if ( semop(sem, (struct sembuf[]) {{0, -1, SEM_UNDO}}, 1) == -1)
00598                     LOGFAIL(VSL_ERRNO, "Cannot release lock: %s!", strerror(errno));
00599                 return 0;
00600             }
00601 
00602         /* Try to read a message */
00603         phead_t head; // for deserialization
00604         int ret;
00605         const int msglen =  sizeof(head.magic) + sizeof(head.type)
00606             + sizeof(head.datalen) + sizeof(sock->client_id) + VSL_KEYLENGTH;
00607         char msg[msglen];
00608         if ( (ret = read(sock->ssh_fd, (void*) &msg, msglen)) != msglen) {
00609             if (errno == EAGAIN) {
00610                 LOG("call would block, returning.");
00611                 if ( semop(sem, (struct sembuf[]) {{0, -1, SEM_UNDO}}, 1) == -1)
00612                     LOGFAIL(VSL_ERRNO, "Cannot release lock: %s!", strerror(errno));
00613                 return VFER_INPROGRESS;
00614             }
00615             if ( semop(sem, (struct sembuf[]) {{0, -1, SEM_UNDO}}, 1) == -1)
00616                 LOGFAIL(VSL_ERRNO, "Cannot release lock: %s!", strerror(errno));
00617             LOGFAIL(VSL_ERRNO, "read failed: %s", strerror(err));
00618         }
00619 
00620         /* Deserialize structure */
00621         uint32_t clid;
00622         char *key = read_objs(msg,
00623                         (void*) &head.magic, sizeof(head.magic),
00624                         (void*) &head.type, sizeof(head.type),
00625                         (void*) &head.datalen, sizeof(head.datalen),
00626                         (void*) &clid, sizeof(clid),
00627                         NULL);
00628         head.datalen = ntohl(head.datalen);
00629         clid = ntohl(clid);
00630 
00631         /* Verify packet */
00632         if(!CHECK_MAGIC(head.magic) ||
00633            head.type != P_SSH_KEY ||
00634            head.datalen != sizeof(clid) + VSL_KEYLENGTH) {
00635             if ( semop(sem, (struct sembuf[]) {{0, -1, SEM_UNDO}}, 1) == -1)
00636                 LOGFAIL(VSL_ERRNO, "Cannot release lock: %s!", strerror(errno));
00637             LOGFAIL(VSL_BADPROT, "invalid packet received");
00638         }
00639 
00640         /* Return if it's the right client */
00641         if (clid == sock->client_id) {
00642             init_keys_s(sock, key);
00643             LOG("Correct secret read from FIFO.");
00644             if ( semop(sem, (struct sembuf[]) {{0, -1, SEM_UNDO}}, 1) == -1)
00645                 LOGFAIL(VSL_ERRNO, "Cannot release lock: %s!", strerror(errno));
00646             return(0);
00647         }
00648 
00649         /* Otherwise store it */
00650         time_t timestamp = time(NULL);
00651         for (int i=0; i < MSG_QUEUE_SIZE; i++)
00652             if (msgqueue[i].timestamp == 0 ||
00653                 timestamp - msgqueue[i].timestamp > MSG_TIMEOUT) {
00654                 memcpy((void*) msgqueue[i].key, (void*) key, VSL_KEYLENGTH);
00655                 msgqueue[i].clid = clid;
00656                 msgqueue[i].timestamp = 0;
00657                 LOG("Cached foreign secret.");
00658                 if ( semop(sem, (struct sembuf[]) {{0, -1, SEM_UNDO}}, 1) == -1)
00659                     LOGFAIL(VSL_ERRNO, "Cannot release lock: %s!", strerror(errno));
00660                 continue;
00661             }
00662         LOGFAIL(VSL_MAXCONN, "Secret cache full!");
00663     }
00664 
00665     LOG("something extremely strange happened.");
00666     abort();
00667 }
00668 
00669 
00670 /******************************************************************
00671  * The following functions belong to the API and were removed from
00672  * vsl_api.c
00673  ******************************************************************/
00674 
00675 
00676 /**
00677  * Authenticates the connection using ssh. This function acts a
00678  * client, the remote side has to call \c vfer_auth_ssh_s() to act as
00679  * the server.
00680  *
00681  * The connection is authenticated by performing the handshake over a
00682  * separate ssh connection. A working ssh binary in the path and ssh
00683  * access to the remote host is required.
00684  *
00685  * After the ssh connection is established, communication with the
00686  * server is done using a FIFO on the server. The pathname of this
00687  * FIFO has to be known beforehand.
00688  *
00689  * For convenience, the function checks whether the given pathname
00690  * agrees with the pathname that the server announces and fails if the
00691  * pathnames don't match. It is not possible to use the announced
00692  * pathname directly because the identity of the server cannot be
00693  * verified at this point and therefore the transmitted pathname is
00694  * untrusted.
00695  *
00696  * For a nonblocking socket, the function returns #VFER_INPROGRESS if
00697  * the call would block. In this case the function has to be called
00698  * again until it returns 0 or an error. vsl_select() can be used to
00699  * test when the socket is ready for the next authentication step.
00700  *
00701  * @param[in] socket VSL socket
00702  * @param[in] host   hostname for the ssh connection
00703  * @param[in] user   username for the ssh connection
00704  * @param[in] path   pathname of the FIFO
00705  * @return
00706  *   - 0 on success
00707  *   - #VFER_INPROGRESS socket is nonblocking and the call would block
00708  *   - #VFER_BADSOCK bad socket argument
00709  *   - #VFER_UNCONN socket is not connected
00710  *   - #VSL_ERRNO if a system call failed (errno is set accordingly)
00711  *   - #VSL_BADPROT protocol error
00712  *   - #VSL_BADPATH pathnames don't match
00713  *   - #VSL_SSHF ssh subprocess failed
00714  **/
00715 int vsl_auth_ssh_c(vsl_sock* socket, const char* user,
00716                    const char* host, const char *path) {
00717     char key[VSL_KEYLENGTH];
00718 
00719     LOG("trying ssh authentication as client");
00720 
00721     switch(socket->status) {
00722     case(C_INITIALIZED):
00723         /* Sent ssh auth request */
00724         TRY(send_ssh_req(socket));
00725         socket->status = C_SSH_REQ_SENT;
00726 
00727     case(C_SSH_REQ_SENT):
00728         /* Receive ssh auth response */
00729         TRY(recv_ssh_res(socket, path));
00730 
00731         /* Fork ssh process */
00732         TRY(fork_ssh(socket, user, host, path));
00733 
00734         /* Generate key */
00735         TRY(gen_key(key));
00736 
00737         socket->status = C_SSH_FORKED;
00738 
00739     case(C_SSH_FORKED):
00740         /* Send secret */
00741         TRY(send_key_ssh(socket, key));
00742         socket->status = C_SSH_SEC_SENT;
00743 
00744         /* Generate session keys */
00745         init_keys_c(socket, key);
00746 
00747     case(C_SSH_SEC_SENT):
00748         /* Close SSH connection */
00749         TRY(close_ssh(socket));
00750         socket->status = C_ESTABLISHED;
00751         socket->auth = VSL_AUTH_SSH;
00752         break;
00753 
00754     default:
00755         /* Invalid state */
00756         LOGFAIL(VFER_BADSOCK, "invalid socket state");
00757     }
00758 
00759     LOG("ssh authentication successfull.");
00760     return(0);
00761 }
00762 
00763 
00764 /**
00765  * Authenticate the connection using ssh. This function acts a server,
00766  * the remote side has to call \c vsl_auth_ssh_c() to act as the client.
00767  * See this function for a more detailed explanation of the handshake.
00768  *
00769  * The FIFO has to be writable for all users who are allowed to
00770  * connect.
00771  *
00772  * Multiple use of the same FIFO by several processes and threads is
00773  * handled as long as vsl_init() is called before the threads are
00774  * started and processes forked.
00775  *
00776  * At most \c sizeof(uint32_t) clients can try to connect at a time.
00777  *
00778  * For a nonblocking socket, the function returns #VFER_INPROGRESS if
00779  * the call would block. In this case the function has to be called
00780  * again until it returns 0 or an error. vsl_select() can be used to
00781  * test when the socket is ready for the next authentication step.
00782  *
00783  * @param[in] socket VSL socket
00784  * @param[in] fifo pathname of the fifo to use.
00785  * @return
00786  *   - 0 on success
00787  *   - #VSL_BADPATH pathname too long
00788  *   - #VFER_INPROGRESS socket is nonblocking and the call would block
00789  *   - #VFER_BADSOCK bad socket argument
00790  *   - #VSL_ERRNO if a system call failed (errno is set accordingly)
00791  *   - #VSL_BADPROT protocol error
00792  *   - #VFER_UNCONN socket is not connected
00793  *   - #VSL_MAXCONN maximum number of open simulatienous FIFO
00794  *     connection attempts reached
00795  **/
00796 int vsl_auth_ssh_s(vsl_sock* socket, const char* fifo) {
00797 
00798     /* Check path length */
00799     if(strlen(fifo) >= MAX_PATH) {
00800         LOGFAIL(VSL_BADPATH, "fifo pathname too long");
00801     }
00802 
00803     /* Check for FIFO existence */
00804     struct stat sbuf;
00805     if(stat(fifo, &sbuf) != 0) {
00806         LOGFAIL(VSL_ERRNO, "can't stat() fifo: %s", strerror(err));
00807     }
00808     if(! S_ISFIFO(sbuf.st_mode)) {
00809         LOGFAIL(VSL_BADPATH, "fifo pathname is not a fifo");
00810     }
00811 
00812     switch(socket->status) {
00813     case(C_INITIALIZED):
00814         /* Receive Packet */
00815         TRY(recv_ssh_req(socket));
00816         socket->status = C_SSH_REQ_RECV;
00817 
00818     case(C_SSH_REQ_RECV):
00819         /* Send Answer */
00820         TRY(send_ssh_res(socket, fifo));
00821         socket->status = C_SSH_RES_SENT;
00822 
00823         /* Open FIFO */
00824         TRY(open_fifo(socket, fifo));
00825 
00826     case(C_SSH_RES_SENT):
00827         /* Receive Key & Close FIFO */
00828         {
00829         int ret;
00830         if ( (ret=recv_key_ssh(socket)) != 0) {
00831             close_fifo(socket);
00832             return ret;
00833         }
00834         if ( (ret=close_fifo(socket)) != 0)
00835             return ret;
00836         }
00837 
00838         socket->status = C_ESTABLISHED;
00839         socket->auth = VSL_AUTH_SSH;
00840         break;
00841 
00842     default:
00843         /* Invalid state */
00844         LOGFAIL(VFER_BADSOCK, "invalid socket state");
00845     }
00846     return(0);
00847 }
00848 
00849 
00850 
00851 /*
00852  * Local Variables:
00853  * compile-command: "gcc -o ../bin/test_vsl -Wall --std=gnu99 vsl_api.c vsl_util.c vsl_fifo.c"
00854  * End:
00855  */
00856 
00857 #endif

Generated on Tue Aug 8 16:07:19 2006 for VFER by  doxygen 1.4.7