~ubuntu-branches/ubuntu/maverick/curl/maverick

« back to all changes in this revision

Viewing changes to lib/ssh.c

  • Committer: Bazaar Package Importer
  • Author(s): Kees Cook
  • Date: 2009-12-11 19:33:21 UTC
  • mfrom: (3.4.2 squeeze) (40.1.2 curl)
  • Revision ID: james.westby@ubuntu.com-20091211193321-tenukopudyznzbjj
* Merge with Debian testing.  Remaining changes:
  - Keep build deps in main:
    - Drop build dependencies: stunnel, libdb4.6-dev, libssh2-1-dev
    - Add build-dependency on openssh-server
    - Drop libssh2-1-dev from libcurl4-openssl-dev's Depends.

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
19
 * KIND, either express or implied.
20
20
 *
21
 
 * $Id: ssh.c,v 1.131 2009-05-11 07:53:38 bagder Exp $
 
21
 * $Id: ssh.c,v 1.141 2009-10-30 22:28:56 bagder Exp $
22
22
 ***************************************************************************/
23
23
 
24
24
/* #define CURL_LIBSSH2_DEBUG */
230
230
  (void)abstract;
231
231
} /* kbd_callback */
232
232
 
233
 
static CURLcode sftp_libssh2_error_to_CURLE(unsigned long err)
 
233
static CURLcode sftp_libssh2_error_to_CURLE(int err)
234
234
{
235
235
  switch (err) {
236
236
    case LIBSSH2_FX_OK:
237
237
      return CURLE_OK;
238
238
 
239
 
    case LIBSSH2_ERROR_ALLOC:
240
 
      return CURLE_OUT_OF_MEMORY;
241
 
 
242
239
    case LIBSSH2_FX_NO_SUCH_FILE:
243
240
    case LIBSSH2_FX_NO_SUCH_PATH:
244
241
      return CURLE_REMOTE_FILE_NOT_FOUND;
301
298
/* This is the ONLY way to change SSH state! */
302
299
static void state(struct connectdata *conn, sshstate nowstate)
303
300
{
304
 
#if defined(CURLDEBUG) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
 
301
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
305
302
  /* for debug purposes */
306
303
  static const char * const names[] = {
307
304
    "SSH_STOP",
308
305
    "SSH_S_STARTUP",
 
306
    "SSH_HOSTKEY",
309
307
    "SSH_AUTHLIST",
310
308
    "SSH_AUTH_PKEY_INIT",
311
309
    "SSH_AUTH_PKEY",
358
356
#endif
359
357
  struct ssh_conn *sshc = &conn->proto.sshc;
360
358
 
361
 
#if defined(CURLDEBUG) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
 
359
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
362
360
  if(sshc->state != nowstate) {
363
361
    infof(conn->data, "SFTP %p state change from %s to %s\n",
364
362
          sshc, names[sshc->state], names[nowstate]);
433
431
  return CURLE_OK;
434
432
}
435
433
 
 
434
#ifdef HAVE_LIBSSH2_KNOWNHOST_API
 
435
static int sshkeycallback(CURL *easy,
 
436
                          const struct curl_khkey *knownkey, /* known */
 
437
                          const struct curl_khkey *foundkey, /* found */
 
438
                          enum curl_khmatch match,
 
439
                          void *clientp)
 
440
{
 
441
  (void)easy;
 
442
  (void)knownkey;
 
443
  (void)foundkey;
 
444
  (void)clientp;
 
445
 
 
446
  /* we only allow perfect matches, and we reject everything else */
 
447
  return (match != CURLKHMATCH_OK)?CURLKHSTAT_REJECT:CURLKHSTAT_FINE;
 
448
}
 
449
#endif
 
450
 
436
451
/*
437
452
 * Earlier libssh2 versions didn't have the ability to seek to 64bit positions
438
453
 * with 32bit size_t.
440
455
#ifdef HAVE_LIBSSH2_SFTP_SEEK64
441
456
#define SFTP_SEEK(x,y) libssh2_sftp_seek64(x, (libssh2_uint64_t)y)
442
457
#else
443
 
#define SFTP_SEEK(x,y) libssh2_sftp_seek(x, y)
 
458
#define SFTP_SEEK(x,y) libssh2_sftp_seek(x, (size_t)y)
444
459
#endif
445
460
 
446
461
/*
483
498
      break;
484
499
    }
485
500
 
486
 
    /* Set libssh2 to non-blocking, since cURL is all non-blocking */
 
501
    /* Set libssh2 to non-blocking, since everything internally is
 
502
       non-blocking */
487
503
    libssh2_session_set_blocking(sshc->ssh_session, 0);
488
504
 
 
505
    state(conn, SSH_HOSTKEY);
 
506
 
 
507
    /* fall-through */
 
508
  case SSH_HOSTKEY:
 
509
 
489
510
#ifdef CURL_LIBSSH2_DEBUG
490
511
    /*
491
512
     * Before we authenticate we should check the hostkey's fingerprint
527
548
      }
528
549
    }
529
550
 
 
551
#ifdef HAVE_LIBSSH2_KNOWNHOST_API
 
552
    if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
 
553
      /* we're asked to verify the host against a file */
 
554
      int keytype;
 
555
      size_t keylen;
 
556
      const char *remotekey = libssh2_session_hostkey(sshc->ssh_session,
 
557
                                                      &keylen, &keytype);
 
558
      int keycheck;
 
559
      int keybit;
 
560
 
 
561
      if(remotekey) {
 
562
        /*
 
563
         * A subject to figure out is what host name we need to pass in here.
 
564
         * What host name does OpenSSH store in its file if an IDN name is
 
565
         * used?
 
566
         */
 
567
        struct libssh2_knownhost *host;
 
568
        enum curl_khmatch keymatch;
 
569
        curl_sshkeycallback func =
 
570
          data->set.ssh_keyfunc?data->set.ssh_keyfunc:sshkeycallback;
 
571
        struct curl_khkey knownkey;
 
572
        struct curl_khkey *knownkeyp = NULL;
 
573
        struct curl_khkey foundkey;
 
574
 
 
575
        keybit = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)?
 
576
          LIBSSH2_KNOWNHOST_KEY_SSHRSA:LIBSSH2_KNOWNHOST_KEY_SSHDSS;
 
577
 
 
578
        keycheck = libssh2_knownhost_check(sshc->kh,
 
579
                                           conn->host.name,
 
580
                                           remotekey, keylen,
 
581
                                           LIBSSH2_KNOWNHOST_TYPE_PLAIN|
 
582
                                           LIBSSH2_KNOWNHOST_KEYENC_RAW|
 
583
                                           keybit,
 
584
                                           &host);
 
585
 
 
586
        infof(data, "SSH host check: %d, key: %s\n", keycheck,
 
587
              (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)?
 
588
              host->key:"<none>");
 
589
 
 
590
        /* setup 'knownkey' */
 
591
        if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) {
 
592
          knownkey.key = host->key;
 
593
          knownkey.len = 0;
 
594
          knownkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)?
 
595
            CURLKHTYPE_RSA : CURLKHTYPE_DSS;
 
596
          knownkeyp = &knownkey;
 
597
        }
 
598
 
 
599
        /* setup 'foundkey' */
 
600
        foundkey.key = remotekey;
 
601
        foundkey.len = keylen;
 
602
        foundkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)?
 
603
          CURLKHTYPE_RSA : CURLKHTYPE_DSS;
 
604
 
 
605
        /*
 
606
         * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the
 
607
         * curl_khmatch enum are ever modified, we need to introduce a
 
608
         * translation table here!
 
609
         */
 
610
        keymatch = (enum curl_khmatch)keycheck;
 
611
 
 
612
        /* Ask the callback how to behave */
 
613
        rc = func(data, knownkeyp, /* from the knownhosts file */
 
614
                  &foundkey, /* from the remote host */
 
615
                  keymatch, data->set.ssh_keyfunc_userp);
 
616
      }
 
617
      else
 
618
        /* no remotekey means failure! */
 
619
        rc = CURLKHSTAT_REJECT;
 
620
 
 
621
      switch(rc) {
 
622
      default: /* unknown return codes will equal reject */
 
623
      case CURLKHSTAT_REJECT:
 
624
        state(conn, SSH_SESSION_FREE);
 
625
      case CURLKHSTAT_DEFER:
 
626
        /* DEFER means bail out but keep the SSH_HOSTKEY state */
 
627
        result = sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
 
628
        break;
 
629
      case CURLKHSTAT_FINE:
 
630
      case CURLKHSTAT_FINE_ADD_TO_FILE:
 
631
        /* proceed */
 
632
        if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) {
 
633
          /* the found host+key didn't match but has been told to be fine
 
634
             anyway so we add it in memory */
 
635
          int addrc = libssh2_knownhost_add(sshc->kh,
 
636
                                            conn->host.name, NULL,
 
637
                                            remotekey, keylen,
 
638
                                            LIBSSH2_KNOWNHOST_TYPE_PLAIN|
 
639
                                            LIBSSH2_KNOWNHOST_KEYENC_RAW|
 
640
                                            keybit, NULL);
 
641
          if(addrc)
 
642
            infof(data, "Warning adding the known host %s failed!\n",
 
643
                  conn->host.name);
 
644
          else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE) {
 
645
            /* now we write the entire in-memory list of known hosts to the
 
646
               known_hosts file */
 
647
            int wrc =
 
648
              libssh2_knownhost_writefile(sshc->kh,
 
649
                                          data->set.str[STRING_SSH_KNOWNHOSTS],
 
650
                                          LIBSSH2_KNOWNHOST_FILE_OPENSSH);
 
651
            if(wrc) {
 
652
              infof(data, "Warning, writing %s failed!\n",
 
653
                    data->set.str[STRING_SSH_KNOWNHOSTS]);
 
654
            }
 
655
          }
 
656
        }
 
657
        break;
 
658
      }
 
659
    }
 
660
#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
 
661
 
530
662
    state(conn, SSH_AUTHLIST);
531
663
    break;
532
664
 
533
665
  case SSH_AUTHLIST:
534
 
    /* TBD - methods to check the host keys need to be done */
535
 
 
536
666
    /*
537
667
     * Figure out authentication methods
538
668
     * NB: As soon as we have provided a username to an openssh server we
1370
1500
    result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL,
1371
1501
                                 FIRSTSOCKET, NULL);
1372
1502
 
 
1503
    /* not set by Curl_setup_transfer to preserve keepon bits */
 
1504
    conn->sockfd = conn->writesockfd;
 
1505
 
1373
1506
    if(result) {
1374
1507
      state(conn, SSH_SFTP_CLOSE);
1375
1508
      sshc->actualcode = result;
1781
1914
  else {
1782
1915
    result = Curl_setup_transfer(conn, FIRSTSOCKET, data->req.size,
1783
1916
                                 FALSE, NULL, -1, NULL);
 
1917
 
 
1918
    /* not set by Curl_setup_transfer to preserve keepon bits */
 
1919
    conn->writesockfd = conn->sockfd;
 
1920
 
 
1921
    /* FIXME: here should be explained why we need it to start the download */
 
1922
    conn->cselect_bits = CURL_CSELECT_IN;
1784
1923
  }
1785
1924
  if(result) {
1786
1925
    state(conn, SSH_SFTP_CLOSE);
1877
2016
    sshc->ssh_channel =
1878
2017
      libssh2_scp_send_ex(sshc->ssh_session, sftp_scp->path,
1879
2018
                          data->set.new_file_perms,
1880
 
                          data->set.infilesize, 0, 0);
 
2019
                          (size_t)data->set.infilesize, 0, 0);
1881
2020
    if(!sshc->ssh_channel) {
1882
2021
      if(libssh2_session_last_errno(sshc->ssh_session) ==
1883
2022
         LIBSSH2_ERROR_EAGAIN) {
1901
2040
    result = Curl_setup_transfer(conn, -1, data->req.size, FALSE, NULL,
1902
2041
                                 FIRSTSOCKET, NULL);
1903
2042
 
 
2043
    /* not set by Curl_setup_transfer to preserve keepon bits */
 
2044
    conn->sockfd = conn->writesockfd;
 
2045
 
1904
2046
    if(result) {
1905
2047
      state(conn, SSH_SCP_CHANNEL_FREE);
1906
2048
      sshc->actualcode = result;
1950
2092
    result = Curl_setup_transfer(conn, FIRSTSOCKET,
1951
2093
                                 bytecount, FALSE, NULL, -1, NULL);
1952
2094
 
 
2095
    /* not set by Curl_setup_transfer to preserve keepon bits */
 
2096
    conn->writesockfd = conn->sockfd;
 
2097
 
 
2098
    /* FIXME: here should be explained why we need it to start the download */
 
2099
    conn->cselect_bits = CURL_CSELECT_IN;
 
2100
 
1953
2101
    if(result) {
1954
2102
      state(conn, SSH_SCP_CHANNEL_FREE);
1955
2103
      sshc->actualcode = result;
2056
2204
    break;
2057
2205
 
2058
2206
  case SSH_SESSION_FREE:
 
2207
#ifdef HAVE_LIBSSH2_KNOWNHOST_API
 
2208
    if(sshc->kh) {
 
2209
      libssh2_knownhost_free(sshc->kh);
 
2210
      sshc->kh = NULL;
 
2211
    }
 
2212
#endif
 
2213
 
2059
2214
    if(sshc->ssh_session) {
2060
2215
      rc = libssh2_session_free(sshc->ssh_session);
2061
2216
      if(rc == LIBSSH2_ERROR_EAGAIN) {
2066
2221
      }
2067
2222
      sshc->ssh_session = NULL;
2068
2223
    }
 
2224
    conn->bits.close = TRUE;
2069
2225
    sshc->nextstate = SSH_NO_STATE;
2070
2226
    state(conn, SSH_STOP);
2071
2227
    result = sshc->actualcode;
2096
2252
                                                       number of sockets */
2097
2253
                               int numsocks)
2098
2254
{
2099
 
#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTIONS
 
2255
#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
2100
2256
  int bitmap = GETSOCK_BLANK;
2101
2257
  (void)numsocks;
2102
2258
 
2103
2259
  sock[0] = conn->sock[FIRSTSOCKET];
2104
2260
 
2105
 
  if(conn->proto.sshc.waitfor & KEEP_RECV)
 
2261
  if(conn->waitfor & KEEP_RECV)
2106
2262
    bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
2107
2263
 
2108
 
  if(conn->proto.sshc.waitfor & KEEP_SEND)
 
2264
  if(conn->waitfor & KEEP_SEND)
2109
2265
    bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
2110
2266
 
2111
2267
  return bitmap;
2123
2279
                                               of sockets */
2124
2280
                       int numsocks)
2125
2281
{
2126
 
#ifndef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTIONS
 
2282
#ifndef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
2127
2283
  (void)conn;
2128
2284
  (void)sock;
2129
2285
  (void)numsocks;
2137
2293
#endif
2138
2294
}
2139
2295
 
2140
 
#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTIONS
 
2296
#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
2141
2297
/*
2142
2298
 * When one of the libssh2 functions has returned LIBSSH2_ERROR_EAGAIN this
2143
2299
 * function is used to figure out in what direction and stores this info so
2149
2305
{
2150
2306
  struct ssh_conn *sshc = &conn->proto.sshc;
2151
2307
  int dir;
2152
 
  if(block && (dir = libssh2_session_block_directions(sshc->ssh_session))) {
 
2308
  if(!block)
 
2309
    conn->waitfor = 0;
 
2310
  else if((dir = libssh2_session_block_directions(sshc->ssh_session))) {
2153
2311
    /* translate the libssh2 define bits into our own bit defines */
2154
 
    sshc->waitfor = ((dir&LIBSSH2_SESSION_BLOCK_INBOUND)?KEEP_RECV:0) |
 
2312
    conn->waitfor = ((dir&LIBSSH2_SESSION_BLOCK_INBOUND)?KEEP_RECV:0) |
2155
2313
      ((dir&LIBSSH2_SESSION_BLOCK_OUTBOUND)?KEEP_SEND:0);
2156
2314
  }
2157
2315
  else
2158
2316
    /* It didn't block or libssh2 didn't reveal in which direction, put back
2159
2317
       the original set */
2160
 
    sshc->waitfor = sshc->orig_waitfor;
 
2318
    conn->waitfor = sshc->orig_waitfor;
2161
2319
}
2162
2320
#else
2163
2321
  /* no libssh2 directional support so we simply don't know */
2188
2346
    bool block;
2189
2347
    result = ssh_statemach_act(conn, &block);
2190
2348
 
2191
 
#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTIONS
 
2349
#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
2192
2350
    if((CURLE_OK == result) && block) {
2193
2351
      int dir = libssh2_session_block_directions(sshc->ssh_session);
2194
2352
      curl_socket_t sock = conn->sock[FIRSTSOCKET];
2278
2436
    return CURLE_FAILED_INIT;
2279
2437
  }
2280
2438
 
 
2439
#ifdef HAVE_LIBSSH2_KNOWNHOST_API
 
2440
  if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
 
2441
    int rc;
 
2442
    ssh->kh = libssh2_knownhost_init(ssh->ssh_session);
 
2443
    if(!ssh->kh) {
 
2444
      /* eeek. TODO: free the ssh_session! */
 
2445
      return CURLE_FAILED_INIT;
 
2446
    }
 
2447
 
 
2448
    /* read all known hosts from there */
 
2449
    rc = libssh2_knownhost_readfile(ssh->kh,
 
2450
                                    data->set.str[STRING_SSH_KNOWNHOSTS],
 
2451
                                    LIBSSH2_KNOWNHOST_FILE_OPENSSH);
 
2452
    if(rc) {
 
2453
      infof(data, "Failed to read known hosts from %s\n",
 
2454
            data->set.str[STRING_SSH_KNOWNHOSTS]);
 
2455
    }
 
2456
  }
 
2457
#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
 
2458
 
2281
2459
#ifdef CURL_LIBSSH2_DEBUG
2282
2460
  libssh2_trace(ssh->ssh_session, ~0);
2283
2461
  infof(data, "SSH socket: %d\n", sock);
2395
2573
static CURLcode scp_disconnect(struct connectdata *conn)
2396
2574
{
2397
2575
  CURLcode result = CURLE_OK;
 
2576
  struct ssh_conn *ssh = &conn->proto.sshc;
2398
2577
 
2399
2578
  Curl_safefree(conn->data->state.proto.ssh);
2400
2579
  conn->data->state.proto.ssh = NULL;
2401
2580
 
2402
 
  if(conn->proto.sshc.ssh_session) {
 
2581
  if(ssh->ssh_session) {
2403
2582
    /* only if there's a session still around to use! */
2404
2583
 
2405
2584
    state(conn, SSH_SESSION_DISCONNECT);