src/vsl_api.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 /**
00008  * @file   vsl_api.c
00009  * @author Nikolaus Rath
00010  * @brief  Implements the VSL api calls
00011  *
00012  * \todo Document consequences of errors: can the function be
00013  * recalled? Is the socket still usable?
00014  */
00015 
00016 #include "vsl.h"
00017 #include "vfer.h"
00018 #include "vsl_api.h"
00019 #include "vsl_util.h"
00020 #include "vsl_ssh.h"
00021 #include "../poly1305aes/poly1305aes.h"
00022 #include <sys/types.h>
00023 #include <signal.h>
00024 #include <sys/wait.h>
00025 
00026 /**
00027  * Initializes the VFER security layer. Has to be called before any
00028  * other VSL functions.
00029  * \return
00030  * - 0 on success
00031  **/
00032 int vsl_init(void) {
00033     return 0;
00034 }
00035 
00036 /**
00037  * Cleanup the ressources used by the security layer. Has to be called
00038  * before the program ends. In case of forked children, every process
00039  * has to call the function before exiting.
00040  * \return
00041  * - 0 on success
00042  **/
00043 int vsl_uninit(void) {
00044     return 0;
00045 }
00046 
00047 /**
00048  * Establishes a secure connection to the specified host using SSL
00049  * Starting Mode. This function has to be called by the client. It
00050  * connects to the remote host via ssh and starts the server. See
00051  * specification for details.
00052  *
00053  * The function requires an initialized VSL socket.
00054  *
00055  * For non blocking sockets, this function might return
00056  * #VFER_INPROGRESS. That means that the next authentikation step
00057  * would block. In this case the caller has to call the function
00058  * repeatedly until it returns 0 or an error. vsl_select() can be
00059  * used to check whether a socket is ready for the next step.
00060  *
00061  * \note The SIGPIPE signal handler is set to ignore by this function.
00062  * If neccessary, the caller has to save the original handler and restore it
00063  * after authentication.
00064  *
00065  * @param[out] socket VSL socket to use
00066  * @param[in] host hostname to connect to
00067  * @param[in] user username for SSH connection, \c NULL to use current user
00068  * @param[in] cmd  program name of the server application
00069  * @param[in] argc  number of arguments in \a argv that are passed to the server application
00070  * @param[in] argv  arguments that is passed to the server application
00071  * \param[out] errmsg if the server returns an error message it is
00072  * stored here. Must be achar array holding at
00073  * least 512 bytes.
00074  *
00075  * \return
00076  *  - 0 on success
00077  *  - #VFER_REFUSED vfer connection attempt refused
00078  *  - #VFER_TIMEOUT vfer connection attempt timedout
00079  *  - #VFER_INPROGRESS if the socket is nonblocking and the call would
00080  *    block.
00081  *  - #VSL_BADPROT in case of a protocol error
00082  *  - #VSL_SSHF if the ssh process failed
00083  *  - #VSL_TEMP if the server signaled a temporary error
00084  *  - #VSL_PERM if the server signaled a permanent error
00085  *  - #VSL_ERRNO if a system call failed (errno is set accordingly)
00086  **/
00087 int vsl_connect_ssh (vsl_sock* socket, const char* host, const char* user,
00088                      const char* cmd, int argc, const char* const *argv,
00089                      char* errmsg) {
00090     unsigned char secret[VSL_KEYLEN];
00091     int port, ret;
00092 
00093     LOG("trying ssh startup as client");
00094 
00095     switch(CONSTAT(socket->status)) {
00096     case(C_INITIALIZED):
00097         /* Fork SSH */
00098         TRY(ssh_fork(socket, host, user, cmd, argc, argv));
00099 
00100         /* Generate shared secret */
00101         TRY(gen_key(secret));
00102 
00103         // Update status
00104         socket->status = C_SSH_FORKED | C_WAIT_FD_W;
00105 
00106         // Testing mode
00107         if ( socket->test_mode &&
00108              !is_blocking(socket) )
00109             LOGFAIL(VFER_INPROGRESS, "returning, testing mode.");
00110 
00111     case(C_SSH_FORKED):
00112         // Write shared secret
00113         if ( (ret=ssh_send_secret(socket, secret)) != 0) {
00114             int bak = errno;
00115             ssh_close(socket, errmsg);
00116             errno = bak;
00117             return ret;
00118         }
00119 
00120         // Update status
00121         socket->status = C_SSH_WROTE_SEC | C_WAIT_FD_R;
00122 
00123         // Testing mode
00124         if ( socket->test_mode &&
00125              !is_blocking(socket) )
00126             LOGFAIL(VFER_INPROGRESS, "returning, testing mode.");
00127 
00128     case(C_SSH_WROTE_SEC):
00129         /* Read port nr. */
00130         if ( (ret=ssh_read_port(socket, &port)) != 0) {
00131             int bak = errno;
00132             ssh_close(socket, errmsg);
00133             errno = bak;
00134             return ret;
00135         }
00136 
00137         // Establish vfer connection in background
00138         TRY(ssh_vfer_connect(socket, host, port));
00139         socket->status = C_SSH_VF_CON | C_WAIT_PID;
00140 
00141         // Testing mode
00142         if ( socket->test_mode &&
00143              !is_blocking(socket) )
00144             LOGFAIL(VFER_INPROGRESS, "returning, testing mode.");
00145 
00146     case C_SSH_VF_CON:
00147         // Close ssh connection
00148         TRY(ssh_close(socket, errmsg));
00149 
00150         //! \todo reactivate this when vfer debugging is done
00151         /*
00152         // wait for vfer_connect
00153         LOG("waiting for vfer_connect...");
00154         int ret;
00155         if ( (ret = vfer_selectmark(socket->vfd, VFER_READABLE | VFER_WRITABLE
00156                                     | VFER_EXCEPTION)) != 0)
00157             LOGFATAL("vfer_selectmark failed: %s!", vfer_errortext(ret));
00158         if ( (ret = vfer_select(1, &(socket->vfd), NULL)) != 1 )
00159             LOGFATAL("vfer_select failed: %s!", vfer_errortext(ret));
00160         if ( vfer_selecttest(socket->vfd) & VFER_EXCEPTION ) {
00161             if (  (ret = vfer_sockerror(socket->vfd)) == VFER_TIMEOUT ||
00162                   ret == VFER_REFUSED)
00163                 LOGFAIL(ret, "vfer_connect failed: %s", vfer_errortext(ret));
00164             LOGFATAL("vfer_connect failed: %s", vfer_errortext(ret));
00165         }
00166         */
00167 
00168         /* Generate keys */
00169         init_keys_c(socket, secret);
00170 
00171         socket->status = C_ESTABLISHED;
00172         socket->auth = VSL_AUTH_SSH;
00173         break;
00174 
00175     default:
00176         /* Invalid state */
00177         LOGFAIL(VFER_BADSOCK, "invalid socket state");
00178     }
00179 
00180     LOG("ssh startup successful.");
00181     return 0;
00182 }
00183 
00184 /**
00185  * Establishes a secure connection to the specified host using SSL
00186  * Starting Mode. This function has to be called by the server. It
00187  * reads the secret from stdin, listens on a free port, prints the
00188  * port nr. to stdout, accepts a connection and daemonizes. See
00189  * specification for details.
00190  *
00191  * This call always blocks.
00192  *
00193  * The function requires an initialized VSL socket.
00194  *
00195  * Note that this function creates a new vfer socket when accepting
00196  * the connection. Therefore, the caller has to make sure that
00197  * vfer_close() is called both for the listening vfer_fd (that was
00198  * passed to vsl_socket()) and the new vfer_fd (which is available
00199  * with vsl_vferfd() after this function returns). The listening vfer
00200  * fd can be closed as soon as vsl_accept_ssh() returns, the accepted
00201  * vfer fd must not be closed before vsl_close() is called.
00202  *
00203  * @param[out] socket VSL socket to use
00204  *
00205  * \return
00206  *  - the vfer fd of the created connection on success (>= 0)
00207  *  - #VSL_BADPROT in case of a protocol error
00208  *  - #VSL_ERRNO if a system call failed (errno is set accordingly)
00209  *  - #VFER_TIMEOUT a timeout occured
00210  **/
00211 int vsl_accept_ssh (vsl_sock* socket) {
00212     int port;
00213     unsigned char secret[VSL_KEYLEN];
00214 
00215     LOG("attempting ssh startup as server...");
00216 
00217     TRY(ssh_read_secret(secret));
00218     TRY(ssh_vfer_listen(socket, &port));
00219     TRY(ssh_send_port(port));
00220     TRY(ssh_vfer_accept(socket));
00221     TRY(ssh_daemonize());
00222 
00223     init_keys_s(socket, secret);
00224 
00225     socket->status = C_ESTABLISHED;
00226     socket->auth = VSL_AUTH_SSH;
00227 
00228     LOG("ssh startup successful.");
00229     return 0;
00230 }
00231 
00232 
00233 /**
00234  * Initializes a VSL socket using an existing and connected vfer fd.
00235  * Before the socket can be used to transfer data, an authentication
00236  * function has to be called to perform the handshake.
00237  *
00238  * @param[in] fd the VFER fd.
00239  * @param[out] socket VSL socket
00240  * \return
00241  *  - 0 on success
00242  *  - #VFER_BADSOCK if \a fd is invalid
00243  */
00244 int vsl_socket(vsl_sock* socket, const vfer_fd fd) {
00245 
00246     LOG("initializing socket...");
00247 
00248     socket->vfd = fd;
00249     socket->status = C_INITIALIZED;
00250     socket->sent_nr = 1;
00251     socket->recv_nr = 0;
00252     socket->test_mode = false;
00253 
00254     // just to make sure the vfd is valid
00255     int nonblocking = 0;
00256     int ret = sizeof(int);
00257     int tmp;
00258     if ( (tmp=vfer_getsockopt(socket->vfd, VFERSO_NONBLOCK, &nonblocking, &ret))
00259          == VFER_BADSOCK )
00260         LOGFAIL(tmp, "received invalid vfer socket!");
00261     else if ( tmp != 0 )
00262         LOGFATAL("vfer_getsockopt failed: %s!", vfer_errortext(tmp));
00263 
00264 
00265     LOG("socket initialized.");
00266     return 0;
00267 }
00268 
00269 
00270 /**
00271  * Destructs the given VSL socket.
00272  *
00273  * This function does nothing at the moment, but it should be called
00274  * as soon as the VSL connection is no longer used in case clean is
00275  * neccessary in later versions.
00276  *
00277  * This function will not close the underlying vfer_fd, it has to be
00278  * closed manually or can be used for normal vfer communication.
00279  *
00280  * @param[in] sock the socket
00281  */
00282 void vsl_close (vsl_sock* sock) {
00283     LOG("closed vsl socket.");
00284 }
00285 
00286 
00287 
00288 /**
00289  * Get the underlying VFER fd of a VSL socket.
00290  *
00291  * @param[in] socket
00292  * @return the vfer fd
00293  */
00294 vfer_fd vsl_vferfd(vsl_sock* socket) {
00295     return socket->vfd;
00296 }
00297 
00298 /**
00299  * Get the maximum size that can be transmitted in a \c vsl_send() or
00300  * \c vsl_recv() call.
00301  *
00302  * @param[in] socket the VSL socket
00303  * @return
00304  *  - the maximum frame size in bytes > 0 on success
00305  */
00306 ssize_t vsl_max_frame_size(vsl_sock* socket) {
00307     return ( (vfer_max_frame_size(socket->vfd) > 1024*1024*1024 ?
00308               1024*1024*1024 :
00309               vfer_max_frame_size(socket->vfd)) - PHEAD_SIZE);
00310 }
00311 
00312 
00313 /**
00314  * Sends \a len bytes from \a buf over a socket.
00315  *
00316  * Equivalent to vfer_send(), but uses the vfer security layer to
00317  * protect the message against modification and eavesdropping.
00318  *
00319  * Due to the used MAC algorithm, the maximum number of messages that
00320  * can be transferred with one socket is \c sizeof(uint64_t).
00321  * Afterwards, #VSL_MAXMSG is returned.
00322  *
00323  * The maximum number of bytes that can be transmitted in a single
00324  * message can be determined with \c vsl_max_frame_size().
00325  *
00326  * If the vfer socket is of type \c SOCK_STREAM, nonblocking IO is
00327  * not supported and #VFER_INVAL returned.
00328  *
00329  * @param[in] sock VSL socket to use
00330  * @param[in] buf buffer to send
00331  * @param[in] len length of the data to send
00332  * @return
00333  *  - len on success
00334  *  - #VSL_TOOBIG message is bigger than vsl_max_frame_size().
00335  *  - #VFER_UNCONN sock is not connected
00336  *  - #VFER_INVAL invalid buf or len
00337  *  - #VFER_WOULDBLOCK socket is non-blocking and requested operation
00338  *    would block
00339  *  - #VSL_MAXMSG maximum message count reached
00340  *  - #VSL_ERRNO if a system call failed (errno is set accordingly)
00341  */
00342 ssize_t vsl_send(vsl_sock* sock, const void * buf, size_t len) {
00343     LOG("attempting to send %zd bytes...", len);
00344 
00345     // Check for nonblocking IO
00346     if ( !is_blocking(sock) &&
00347          vfer_is_stream(sock->vfd) )
00348         LOGFAIL(VFER_INVAL, "nonblocking IO not supported for streamed connections!");
00349 
00350 
00351 
00352     if ( sock->sent_nr == 0 )
00353          LOGFAIL(VSL_MAXMSG, "maximum message count reached!");
00354 
00355     // set actual packet length
00356     if ( len > vsl_max_frame_size(sock) )
00357         LOGFAIL(VSL_TOOBIG, "Message too large.");
00358 
00359     /* create header */
00360     phead_t msg;
00361     msg.type = P_DATA;
00362     FILL_MAGIC(msg.magic);
00363     msg.datalen = htonl(len);
00364     msg.msgnr = htonll(sock->sent_nr++);
00365 
00366     /* Serialize */
00367     size_t buflen;
00368     unsigned char *sbuf;
00369     if ( (sbuf = write_objs(&buflen,
00370                             (void*) &msg.msgnr, sizeof(msg.msgnr),
00371                             (void*) &msg.hash, sizeof(msg.hash),
00372                             (void*) &msg.magic, sizeof(msg.magic),
00373                             (void*) &msg.type, sizeof(msg.type),
00374                             (void*) &msg.datalen, sizeof(msg.datalen),
00375                             buf, len, NULL)) == NULL)
00376          LOGFAIL(VSL_ERRNO, "malloc failed: %s", strerror(err));
00377 
00378     /* Encrypt */
00379     LOG("encrypting...");
00380     unsigned char ctr[16];
00381     memset((void*) ctr, 0, 16);
00382     memcpy((void*) ctr, (void*) sbuf, sizeof(msg.msgnr));
00383     aes_ctr(ctr, sock->enc_send_key, sbuf + PHEAD_SIZE, len);
00384 
00385     /* Authenticate (including msgnr) */
00386     LOG("Calculating MAC...");
00387     unsigned char hash[sizeof(msg.hash)];
00388     memset((void*) ctr, 0, 16);
00389     memcpy((void*) ctr, (void*) sbuf, sizeof(msg.msgnr));
00390     memset(sbuf + sizeof(msg.msgnr), 0, sizeof(msg.hash));
00391     poly1305aes_authenticate(hash, sock->auth_send_key, ctr,
00392                              sbuf, buflen);
00393     memcpy(sbuf + sizeof(msg.msgnr), hash, sizeof(msg.hash));
00394 
00395 
00396     /* Send  */
00397     LOG("Sending total message of size %zd bytes.", buflen);
00398     int ret;
00399     if ( (ret=transfer(sock, (transmit_fn) vfer_send, sbuf, buflen, buflen)) < 0) {
00400         free(sbuf);
00401         return ret;
00402     }
00403     free(sbuf);
00404 
00405     LOG("message sent.");
00406     return len;
00407 }
00408 
00409 
00410 /**
00411  * Sends \a len bytes from file \a fd starting at \a offset
00412  * buf over socket \a sock.
00413  *
00414  * Equivalent to vfer_sendfile(), but uses the vfer security layer to
00415  * protect the message against modification and eavesdropping.
00416  *
00417  * Due to the used MAC algorithm, the maximum number of messages that
00418  * can be transferred with one socket is \c sizeof(uint64_t). So it
00419  * may happen that large files cannot be transferred completely. In
00420  * this case #VSL_MAXMSG is returned after the maximum number of bytes
00421  * has been transferred.
00422  *
00423  * @param[in] sock the VFS socket to use
00424  * @param[out] fd file descriptor for reading
00425  * @param[in] offset offset to start reading
00426  * \param[in] len number of bytes to write
00427  * @return
00428  *      - Returns the actual number of bytes sent on success (a positive number)
00429  *      - errors like vsl_send()
00430  */
00431 ssize_t vsl_sendfile(vsl_sock* sock, int fd, off_t offset, size_t len) {
00432     return vfer__sendfile( (vfer_trnsm_fn) vsl_send, (vfer_trnsm_arg) sock,
00433                            vsl_max_frame_size(sock),fd, offset, len );
00434 }
00435 
00436 
00437 /**
00438  * Reads \a len bytes from \a buf on a connected socket.
00439  *
00440  * Equivalent to vfer_recv(), but uses the vfer security layer to
00441  * protect the message against modification and eavesdropping.
00442  *
00443  * Due to the used MAC algorithm, the maximum number of messages that
00444  * can be transferred with one socket is \c sizeof(uint64_t).
00445  *
00446  * The maximum number of bytes that can be transmitted in one message
00447  * can be determined with  vsl_max_frame_size().
00448  *
00449  * Note that if the incoming message is larger than \a len, the
00450  * message cannot be verified and #VSL_BADPROT will be returned.
00451  *
00452  * If the vfer socket is of type \c SOCK_STREAM, nonblocking IO is
00453  * not supported and #VFER_INVAL returned.
00454  *
00455  * @param[in] sock the socket to use
00456  * @param[out] buf a pointer to a buffer into which to receive
00457  * @param[in] len maximum length of data to receive into buf
00458  * @return
00459  *  - Returns the actual number of bytes sent on success (a positive number)
00460  *  - #VFER_UNCONN sock is not connected
00461  *  - #VFER_INVAL invalid buf or len.
00462  *  - #VFER_WOULDBLOCK socket is non-blocking and requested operation
00463  *    would block
00464  *  - #VSL_BADPROT in case of a protocol error, may also be caused by
00465  *    a forged message
00466  *  - #VSL_TOOBIG message is larger than \a len.
00467  *  - #VSL_ERRNO if a system call failed (errno is set accordingly)
00468  */
00469 ssize_t vsl_recv(vsl_sock* sock, void *buf, size_t len) {
00470     unsigned char* rbuf;
00471     size_t to_read;
00472 
00473     // Check for nonblocking IO
00474     if ( !is_blocking(sock) &&
00475          vfer_is_stream(sock->vfd) )
00476           LOGFAIL(VFER_INVAL, "nonblocking IO not supported for streamed connections!");
00477 
00478 
00479     // Start reading a new message
00480     if ( (rbuf = malloc(vfer_max_frame_size(sock->vfd))) == NULL)
00481         LOGFAIL(VSL_ERRNO, "malloc failed: %s", strerror(err));
00482 
00483     // Determine bytes to read
00484     if ( vfer_is_stream(sock->vfd) ) {
00485         LOG("retrieving header...");
00486         to_read = PHEAD_SIZE;
00487     }
00488     else {
00489         LOG("trying to retrieve %zd + %zd bytes (body+header)...",
00490             len, PHEAD_SIZE);
00491         to_read = len + PHEAD_SIZE;
00492     }
00493 
00494     // Read header
00495     ssize_t rcnt;
00496     if ( (rcnt=transfer(sock, (transmit_fn) vfer_recv, rbuf, PHEAD_SIZE, to_read)) < 0) {
00497         free(rbuf);
00498         return rcnt;
00499     }
00500 
00501     /* Deserialize */
00502     phead_t head;
00503     unsigned char* body;
00504     if ( (body = read_objs(rbuf, rcnt,
00505                      (void*) &head.msgnr, sizeof(head.msgnr),
00506                      (void*) &head.hash, sizeof(head.hash),
00507                      (void*) &head.magic, sizeof(head.magic),
00508                      (void*) &head.type, sizeof(head.type),
00509                      (void*) &head.datalen, sizeof(head.datalen),
00510                            NULL)) == NULL ) {
00511         // We should have caugth this error above
00512         free(rbuf);
00513         LOGFATAL("message too short!");
00514     }
00515     head.datalen = ntohl(head.datalen);
00516     LOG("actual body length %zd bytes.", head.datalen);
00517 
00518     // Stream: Retrieve body
00519     if ( vfer_is_stream(sock->vfd) ) {
00520         LOG("retrieving body...");
00521         if ( (rcnt = transfer(sock, (transmit_fn) vfer_recv, body, head.datalen, head.datalen)) < 0 ) {
00522             free(rbuf);
00523             return rcnt;
00524         }
00525     }
00526 
00527     // Datagram: Check packet completeness
00528     else if ( head.datalen != rcnt - PHEAD_SIZE ) {
00529         free(rbuf);
00530         LOGFAIL(VSL_BADPROT, "invalid message received (too short)!");
00531     }
00532 
00533     // Check if buf is large enough
00534     if ( head.datalen > len ) {
00535         free(rbuf);
00536         LOGFAIL(VSL_TOOBIG, "message does not fit into given buf!");
00537     }
00538 
00539     // Check if message size conforms to the protocol
00540     if ( head.datalen > vsl_max_frame_size(sock) ) {
00541         free(rbuf);
00542         LOGFAIL(VSL_BADPROT, "invalid message received (too long)!");
00543     }
00544 
00545     /* Check magic */
00546     if ( !CHECK_MAGIC(head.magic) ) {
00547         free(rbuf);
00548         LOGFAIL(VSL_BADPROT, "invalid message received (wrong magic)!");
00549     }
00550 
00551     /* Check type */
00552     if ( head.type != P_DATA ) {
00553         free(rbuf);
00554         LOGFAIL(VSL_BADPROT, "invalid message received (wrong type)!");
00555     }
00556 
00557     /* Check message nr  */
00558     if ( ntohll(head.msgnr) <= sock->recv_nr ) {
00559         free(rbuf);
00560         LOGFAIL(VSL_BADPROT, "invalid message received (old msgnr)!");
00561     }
00562     sock->recv_nr = ntohll(head.msgnr);
00563 
00564     /* Check MAC */
00565     LOG("Checking MAC...");
00566     unsigned char ctr[16];
00567     memset((void*) ctr, 0, 16);
00568     memcpy((void*) ctr, (void*) rbuf, sizeof(head.msgnr));
00569     memset(rbuf + sizeof(head.msgnr), 0, sizeof(head.hash));
00570     if ( poly1305aes_verify(head.hash, sock->auth_recv_key, ctr,
00571                             rbuf, head.datalen + PHEAD_SIZE) == 0 ) {
00572         free(rbuf);
00573         LOGFAIL(VSL_BADPROT, "invalid message received (wrong MAC)!");
00574     }
00575 
00576     /* Decrypt */
00577     LOG("Decrypting body...");
00578     memset((void*) ctr, 0, 16);
00579     memcpy((void*) ctr, (void*) rbuf, sizeof(head.msgnr));
00580     aes_ctr(ctr, sock->enc_recv_key, body, head.datalen);
00581 
00582     /* Copy message */
00583     memcpy(buf, body, head.datalen);
00584     free(rbuf);
00585 
00586     LOG("message received.");
00587     return head.datalen;
00588 }
00589 
00590 /**
00591  * Reads \a len bytes from \a buf on a connected socket and writes
00592  * them into the specified fd, starting at \a offset.
00593  *
00594  * Equivalent to vfer_recvfile(), but uses the vfer security layer to
00595  * protect the message against modification and eavesdropping.
00596  *
00597  * Due to the used MAC algorithm, the maximum number of messages that
00598  * can be transferred with one socket is \c sizeof(uint64_t).
00599  * Afterwards, #VSL_MAXMSG is returned.
00600  *
00601  * @param[in] sock the VFS socket to use
00602  * @param[out] fd file descriptor for writing
00603  * @param[in] offset offset to start writing
00604  * \param[in] len number of bytes to read
00605  * @return
00606  *      - number of bytes read on success
00607  *      - errors like vsl_recv()
00608  */
00609 ssize_t vsl_recvfile(vsl_sock* sock, int fd, off_t offset, size_t len) {
00610     return vfer__recvfile( (vfer_trnsm_fn) vsl_recv, (vfer_trnsm_arg) sock,
00611                            vsl_max_frame_size(sock),fd, offset, len);
00612 }
00613 
00614 
00615 /**
00616  * Marks socket with the conditions to be tested by the select call.
00617  *
00618  * Called prior to using vsl_select(). Mark is a bitwise OR of
00619  *  - #VSL_READABLE if the socket has received a message
00620  *  - #VSL_WRITABLE if the socket can send a message
00621  *  - #VSL_AUTHABLE if the socket is ready for the next
00622  *    authenticiation step by one of the VLS authentication functions.
00623  *  - #VSL_EXCEPTION if an error occured for the socket
00624  *
00625  * @param[in] socket VSL socket to mark
00626  * @param[in] mark a mark to associate with the socket
00627  * @return
00628  *      - 0 on succes
00629  *      - #VFER_INVAL invalid mark value
00630  */
00631 int vsl_selectmark(vsl_sock* socket, int mark) {
00632     LOG("entered.");
00633     if ((mark & ~(VSL_READABLE | VSL_WRITABLE | VSL_EXCEPTION | VSL_AUTHABLE)) != 0)
00634         LOGFAIL(VFER_INVAL, "invalid mark value!");
00635 
00636     socket->selectmark = mark;
00637     LOG("marked socket.");
00638     return 0;
00639 }
00640 
00641 /**
00642  * Returns the result of the vsl_select() call for socket.
00643  *
00644  * The return value will be a bitwise OR of the constants described in
00645  * vsl_selectmark(), depending on how \a sock was marked and the
00646  * result of the vsl_select() call.
00647  *
00648  * @param[in] sock the VSL socket
00649  * @return
00650  *      - bitwise OR of marks for socket \a sock on succes
00651  *      - #VFER_BADSOCK bad socket
00652  */
00653 int vsl_selecttest(vsl_sock* sock) {
00654     return sock->selectres;
00655 }
00656 
00657 /**
00658  * Returns the number of VSL sockets in an array that satisfy the
00659  * marked conditions.
00660  *
00661  * \a timeout is the maximum time to wait before the call returns. If
00662  * \a timeout is \c NULL the call blocks indefinitely. A timeout value of
00663  * zero can be used to effect a poll operation.
00664  *
00665  * \warning Due to some implementation problems, this function might
00666  * return immediately with #VFER_INPROGRESS. That means that at the
00667  * moment no socket is ready, and that it is not possible to wait for
00668  * all specified sockets at the same time. In this case, the
00669  * application has to poll the sockets by repetedly calling this
00670  * function. However, this behaviour can only occur if \a len is
00671  * bigger than 1 and at least one socket is marked with #VSL_AUTHABLE.
00672  *
00673  * @param[in] len     length of socks array
00674  * @param[in] socks   an array of sockets to check for marked conditions
00675  * @param[in] stimeout a timeout value to control waiting time
00676  * @return
00677  *    - a positive number if some sockets satisfy the marked
00678  *      conditions (\b not the number of sockets).
00679  *    - #VFER_INVAL timeout value is invalid
00680  *    - #VFER_INPROGRESS blocking select not possible
00681  *    - #VSL_ERRNO if a system call failed (errno is set accordingly)
00682  */
00683 int vsl_select(int len, vsl_sock** socks, struct timeval *stimeout) {
00684     LOG("entered.");
00685 
00686     vfer_fd vfer_fds[len];
00687     fd_set read_fds, write_fds, except_fds;
00688     int vfer_fd_count=0,
00689         file_fd_count=-1,
00690         pid_count=0;
00691 
00692     FD_ZERO(&read_fds);
00693     FD_ZERO(&write_fds);
00694     FD_ZERO(&except_fds);
00695 
00696     /* Loop over all sockets to determine what we're waiting for */
00697     for (int i=0; i < len; i++) {
00698         vsl_sock* sock = socks[i];
00699         sock->selectres = 0;
00700 
00701         /* Connected sockets can only be ready for reading,
00702          * writing or exceptions */
00703         if ( sock->status == C_ESTABLISHED ) {
00704             int mark = 0, ret;
00705             mark |= VSL_READABLE  & sock->selectmark ? VFER_READABLE  : 0;
00706             mark |= VSL_WRITABLE & sock->selectmark ? VFER_WRITABLE : 0;
00707             mark |= VSL_EXCEPTION & sock->selectmark ? VFER_EXCEPTION : 0;
00708             if ((ret=vfer_selectmark(sock->vfd, mark)) < 0)
00709                 LOGFATAL("unknown vfer error: %s", vfer_errortext(ret));
00710             vfer_fds[vfer_fd_count++] = sock->vfd;
00711             LOG("adding socket to vfer select list.");
00712             continue;
00713         }
00714 
00715         /* If the socket is not yet connected, we have to
00716          * check the current state */
00717         if ( sock->selectmark & VSL_AUTHABLE ) {
00718             int ret;
00719 
00720             // Ready to start auth, no need for select
00721             if (sock->status == C_INITIALIZED) {
00722                 sock->selectres = VSL_AUTHABLE;
00723                 LOGFAIL(1, "socket in initialized state, returning now.");
00724             }
00725 
00726             switch (CONWAIT(sock->status)) {
00727 
00728             case C_WAIT_FD_R:
00729                 // Waiting to read from fd
00730                 FD_SET(sock->ssh_stdout_fd, &read_fds);
00731                 FD_SET(sock->ssh_stdout_fd, &except_fds);
00732                 LOG("adding socket to read fd set.");
00733                 if ( sock->ssh_stdout_fd > file_fd_count )
00734                     file_fd_count = sock->ssh_stdout_fd;
00735                 continue;
00736 
00737             case C_WAIT_FD_W:
00738                 // Waiting to write to fd
00739                 FD_SET(sock->ssh_stdin_fd, &write_fds);
00740                 FD_SET(sock->ssh_stdin_fd, &except_fds);
00741                 LOG("adding socket to write fd set.");
00742                 if ( sock->ssh_stdin_fd > file_fd_count )
00743                     file_fd_count = sock->ssh_stdin_fd;
00744                 continue;
00745 
00746             case C_WAIT_PID:
00747                 // Waiting for process
00748                 if ( sock->ssh_pid == 0 )
00749                     LOGFAIL(1, "already waited for pid, returning now.");
00750                 pid_count++;
00751                 LOG("adding socket to pid wait set.");
00752                 continue;
00753 
00754             case C_WAIT_VFD_R:
00755                 // Waiting to read vfer packet
00756                 if ((ret=vfer_selectmark(sock->vfd, VFER_READABLE | VFER_EXCEPTION)) < 0)
00757                     LOGFATAL("unknown vfer error: %s", vfer_errortext(ret));
00758                 vfer_fds[vfer_fd_count++] = sock->vfd;
00759                 LOG("adding socket to vfer select list.");
00760                 continue;
00761 
00762 
00763             case C_WAIT_VFD_W:
00764                 // Waiting to send vfer packet
00765                 if ((ret=vfer_selectmark(sock->vfd, VFER_WRITABLE | VFER_EXCEPTION)) < 0)
00766                     LOGFATAL("unknown vfer error: %s", vfer_errortext(ret));
00767                 vfer_fds[vfer_fd_count++] = sock->vfd;
00768                 LOG("adding socket to vfer select list.");
00769                 continue;
00770 
00771             }
00772         }
00773     }
00774     file_fd_count++;
00775 
00776 
00777     /* Test if we have to poll */
00778     struct timeval *timeout;
00779     if ( (vfer_fd_count != 0 &&
00780           file_fd_count != 0) ||
00781          (vfer_fd_count != 0 &&
00782           pid_count != 0) ||
00783          (file_fd_count != 0 &&
00784           pid_count != 0) ) {
00785         LOG("polling neccessary.");
00786         timeout = &((struct timeval) { 0, 0 });
00787     }
00788     else {
00789         LOG("no polling neccessary.");
00790         timeout = stimeout;
00791     }
00792 
00793 
00794     /* Select VFER sockets */
00795     int ret, cnt=0;
00796     if (vfer_fd_count) {
00797         LOG("calling vfer_select...");
00798         if ( (cnt = vfer_select(vfer_fd_count, vfer_fds, timeout)) == -1 )
00799             LOGFAIL(VSL_ERRNO, "vfer_select failed: %s!", vfer_errortext(cnt));
00800         LOG("%d sockets ready so far..", cnt);
00801 
00802         /* Save marks */
00803         for(int i=0; i < len; i++) {
00804             vsl_sock* sock = socks[i];
00805             if ( sock->status == C_ESTABLISHED ) {
00806                 if ( (ret = vfer_selecttest(sock->vfd)) < 0)
00807                     LOGFATAL("unknown vfer error: %s", vfer_errortext(ret));
00808                 if ( ret & VFER_READABLE )
00809                     sock->selectres |= VSL_READABLE;
00810                 if ( ret & VFER_WRITABLE )
00811                     sock->selectres |= VSL_WRITABLE;
00812                 if ( ret & VFER_EXCEPTION )
00813                     sock->selectres |= VSL_EXCEPTION;
00814             }
00815 
00816 
00817             if ( sock->selectmark & VSL_AUTHABLE &&
00818                  ( CONWAIT(sock->status) == C_WAIT_VFD_W ||
00819                    CONWAIT(sock->status) == C_WAIT_VFD_R ) ) {
00820                 if ( (ret = vfer_selecttest(sock->vfd)) < 0)
00821                     LOGFATAL("unknown vfer error: %s", vfer_errortext(ret));
00822                 if (ret)
00823                     sock->selectres |= VSL_AUTHABLE;
00824             }
00825         }
00826     }
00827 
00828 
00829     /* Select FIFOs */
00830     if (file_fd_count) {
00831         LOG("calling select...");
00832         if ( (cnt += select(file_fd_count, &read_fds, &write_fds, &except_fds,
00833                             timeout)) == -1)
00834             LOGFAIL(VSL_ERRNO, "select call failed: %s!", strerror(errno));
00835         LOG("%d sockets ready so far..", cnt);
00836 
00837         /* Save marks */
00838         for(int i=0; i < len; i++) {
00839             vsl_sock* sock = socks[i];
00840             if ( sock->selectmark & VSL_AUTHABLE &&
00841                  CONWAIT(sock->status) == C_WAIT_FD_W )
00842                 if ( FD_ISSET(sock->ssh_stdin_fd, &write_fds) ||
00843                      FD_ISSET(sock->ssh_stdin_fd, &except_fds) )
00844                     sock->selectres |= VSL_AUTHABLE;
00845 
00846             if ( sock->selectmark & VSL_AUTHABLE &&
00847                  CONWAIT(sock->status) == C_WAIT_FD_R )
00848                 if ( FD_ISSET(sock->ssh_stdout_fd, &read_fds) ||
00849                      FD_ISSET(sock->ssh_stdout_fd, &except_fds) )
00850                     sock->selectres |= VSL_AUTHABLE;
00851         }
00852 
00853     }
00854 
00855 
00856     /* Check PIDs */
00857     if (pid_count) {
00858         LOG("Checking PIDs...");
00859         for(int i=0; i < len; i++) {
00860             vsl_sock* sock = socks[i];
00861             if ( (sock->selectmark & VSL_AUTHABLE) &&
00862                  CONWAIT(sock->status) == C_WAIT_PID) {
00863                 LOG("trying to wait for pid %d..", sock->ssh_pid);
00864                 // We ignore errors here, they are handled in
00865                 // ssh_close()
00866                 if( waitpid(sock->ssh_pid, &(sock->ssh_pid_stat), WNOHANG) > 0) {
00867                     LOG("successful.");
00868                     sock->ssh_pid = 0;
00869                     cnt++;
00870                     sock->selectres |= VSL_AUTHABLE;
00871                 }
00872                 else
00873                     LOG("still running.");
00874             }
00875         }
00876         LOG("%d sockets ready so far..", cnt);
00877     }
00878 
00879 
00880     LOG("returning.");
00881     if (timeout == stimeout || cnt)
00882         return cnt;
00883     else
00884         return VFER_INPROGRESS;
00885 }
00886 
00887 
00888 
00889 /**
00890  * Returns a text description of the error code \a err. If the
00891  * errorcode is not specific to the security api, vfer_errortext() is
00892  * called automatically instead.
00893  *
00894  * @param[in] err error code
00895  * @return
00896  *      a pointer to a text description of \a err
00897  */
00898 char* vsl_errortext(int err) {
00899     switch (err) {
00900     case VSL_BADPROT:
00901         return "protocol error or forged message";
00902     case VSL_MAXMSG:
00903         return "maximum message count exceeded";
00904     case VSL_ERRNO:
00905         return strerror(errno);
00906     case VSL_SSHF:
00907         return "ssh subprocess failed";
00908     case VSL_TEMP:
00909         return "server signaled temporary error";
00910     case VSL_PERM:
00911         return "server signaled permanent error";
00912     case VSL_TOOBIG:
00913         return "message too big";
00914     default:
00915         return vfer_errortext(err);
00916     }
00917 }
00918 
00919 
00920 /**
00921  * Activates debugging messages
00922  *
00923  * \param[in] fp stream to send debugging messages to, \c NULL deactivates
00924  * debugging.
00925  * \param[in] vfer If not \c NULL, configures vfer to send its
00926  * debugging output (level given by the contents of \a vfer) to the
00927  * same file.
00928  * */
00929 FILE* debug = NULL;
00930 void vsl_debug (FILE* fp, const char* vfer) {
00931     // Handle disabling
00932     if (fp == NULL) {
00933         debug = NULL;
00934         vfer_debug(NULL, NULL, "");
00935         return;
00936     }
00937 
00938     /* dup the fd so it isn't lost when we fork */
00939     int fd;
00940     if ( (fd = dup(fileno(fp))) == -1 ) {
00941         fprintf(stderr, "Cannot dup fd: %s!\n", strerror(errno));
00942         abort();
00943     }
00944     if ( (debug = fdopen(fd, "a")) == NULL ) {
00945         fprintf(stderr, "Cannot fdopen fd %d: %s!\n", fd, strerror(errno));
00946         abort();
00947     }
00948     if (vfer == NULL)
00949         vfer = "";
00950     vfer_debug(debug, debug, vfer);
00951 }
00952 
00953 /**
00954  * Activates some behaviour that is only useful for testing
00955  * VSL internals:
00956  *
00957  *  - vsl_connect_ssh() returns after each step in non blocking mode
00958  *
00959  * \param[in] sock socket to mark
00960  **/
00961 void vsl_test_mode(vsl_sock* sock) {
00962     LOG("Activating test mode.");
00963     sock->test_mode = true;
00964 }
00965 
00966 /**
00967  * Like vsl_select(), but automatically polls every \a usec
00968  * microseconds when vsl_select() would return #VFER_INPROGRESS.
00969  *
00970  * @param[in] len     length of socks array
00971  * @param[in] socks   an array of sockets to check for marked conditions
00972  * @param[in] stimeout a timeout value to control waiting time
00973  * @param[in] usec polling intervall in microseconds
00974  * @return
00975  *    - number of sockets in array socks that satisfy the marked conditions
00976  *    - #VFER_INVAL timeout value is invalid
00977  *    - #VSL_ERRNO if a system call failed (errno is set accordingly)
00978  */
00979 int vsl_selectpoll(int len, vsl_sock** socks, struct timeval *stimeout, int usec) {
00980     int ret;
00981     struct timeval start;
00982     struct timeval now;
00983     GET_TIME_OF_DAY(&start);
00984     do {
00985         ret = vsl_select(len, socks, stimeout);
00986         if ( ret == VFER_INPROGRESS ) {
00987             GET_TIME_OF_DAY(&now);
00988             if ( TIMEVAL_MS(*stimeout) > TIMEVAL_MS_DIFF(start, now) )
00989                 return 0;
00990             usleep(usec);
00991             continue;
00992         }
00993         else
00994             return ret;
00995     } while(1);
00996 }
00997 
00998 
00999 
01000 
01001 
01002 /*
01003  * Local Variables:
01004  * compile-command: "cd ..; make tests file_xfer"
01005  * compilation-search-path: ("..")
01006  * End:
01007  */

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