Commit 105ec79b authored by Daniel Stenberg's avatar Daniel Stenberg
Browse files

James Cone's efforts to add another netrc parsing "mode"

parent c759d842
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -49,7 +49,9 @@

struct memdebug {
  int size;
  char mem[1];
  double mem[1];
  /* I'm hoping this is the thing with the strictest alignment
   * requirements.  That also means we waste some space :-( */
};

/*
+46 −6
Original line number Diff line number Diff line
@@ -79,11 +79,14 @@ int Curl_parsenetrc(char *host,
  char netrcbuffer[256];
  int retcode=1;

  int specific_login = (login[0] != 0);
  
  char *home = NULL; 
  int state=NOTHING;

  char state_login=0;
  char state_password=0;
  char state_login=0;      /* Found a login keyword */
  char state_password=0;   /* Found a password keyword */
  char state_our_login=0;  /* With specific_login, found *our* login name */

#define NETRC DOT_CHAR "netrc"

@@ -116,6 +119,30 @@ int Curl_parsenetrc(char *host,

  sprintf(netrcbuffer, "%s%s%s", home, DIR_CHAR, NETRC);

#ifdef MALLOCDEBUG
  {
    /* This is a hack to allow testing.
     * If compiled with --enable-debug and CURL_DEBUG_NETRC is defined,
     * then it's the path to a substitute .netrc for testing purposes *only* */

    char *override = curl_getenv("CURL_DEBUG_NETRC");

    if (override != NULL) {
      printf("NETRC: overridden .netrc file: %s\n", home);

      if (strlen(override)+1 > sizeof(netrcbuffer)) {
        free(override);
        if(NULL==pw)
          free(home);

        return -1;
      }
      strcpy(netrcbuffer, override);
      free(override);
    }
  }
#endif /* MALLOCDEBUG */

  file = fopen(netrcbuffer, "r");
  if(file) {
    char *tok;
@@ -123,6 +150,10 @@ int Curl_parsenetrc(char *host,
    while(fgets(netrcbuffer, sizeof(netrcbuffer), file)) {
      tok=strtok_r(netrcbuffer, " \t\n", &tok_buf);
      while(tok) {

        if (login[0] && password[0])
          goto done;

	switch(state) {
	case NOTHING:
	  if(strequal("machine", tok)) {
@@ -149,17 +180,23 @@ int Curl_parsenetrc(char *host,
	case HOSTVALID:
	  /* we are now parsing sub-keywords concerning "our" host */
	  if(state_login) {
            if (specific_login) {
              state_our_login = strequal(login, tok);
            }else{
              strncpy(login, tok, LOGINSIZE-1);
#ifdef _NETRC_DEBUG
	      printf("LOGIN: %s\n", login);
#endif
            }
	    state_login=0;
	  }
	  else if(state_password) {
            if (state_our_login || !specific_login) {
              strncpy(password, tok, PASSWORDSIZE-1);
#ifdef _NETRC_DEBUG
              printf("PASSWORD: %s\n", password);
#endif
            }
	    state_password=0;
	  }
	  else if(strequal("login", tok))
@@ -169,13 +206,16 @@ int Curl_parsenetrc(char *host,
	  else if(strequal("machine", tok)) {
	    /* ok, there's machine here go => */
	    state = HOSTFOUND;
            state_our_login = 0;
	  }
	  break;
	} /* switch (state) */

	tok = strtok_r(NULL, " \t\n", &tok_buf);
      } /* while (tok) */
    } /* while fgets() */

done:
    fclose(file);
  }

+5 −0
Original line number Diff line number Diff line
@@ -25,4 +25,9 @@
int Curl_parsenetrc(char *host,
                    char *login,
                    char *password);
  /* Assume: password[0]=0, host[0] != 0.
   * If login[0] = 0, search for login and password within a machine section
   * in the netrc.
   * If login[0] != 0, search for password within machine and login.
   */
#endif
+162 −131
Original line number Diff line number Diff line
@@ -441,7 +441,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...)
    /*
     * Parse the $HOME/.netrc file
     */
    data->set.use_netrc = va_arg(param, long)?TRUE:FALSE;
    data->set.use_netrc = va_arg(param, long);
    break;
  case CURLOPT_FOLLOWLOCATION:
    /*
@@ -1351,7 +1351,6 @@ static CURLcode CreateConnection(struct SessionHandle *data,
  char resumerange[40]="";
  struct connectdata *conn;
  struct connectdata *conn_temp;
  char endbracket;
  int urllen;
  Curl_addrinfo *hostaddr;
#ifdef HAVE_ALARM
@@ -1533,35 +1532,12 @@ static CURLcode CreateConnection(struct SessionHandle *data,

  buf = data->state.buffer; /* this is our buffer */

  /*************************************************************
   * Take care of user and password authentication stuff
   *************************************************************/

  if(conn->bits.user_passwd && !data->set.use_netrc) {
    data->state.user[0] =0;
    data->state.passwd[0]=0;

    if(*data->set.userpwd != ':') {
      /* the name is given, get user+password */
      sscanf(data->set.userpwd, "%127[^:]:%127[^\n]",
             data->state.user, data->state.passwd);
    }
    else
      /* no name given, get the password only */
      sscanf(data->set.userpwd+1, "%127[^\n]", data->state.passwd);

    /* check for password, if no ask for one */
    if( !data->state.passwd[0] ) {
      if(!data->set.fpasswd || 
         data->set.fpasswd(data->set.passwd_client,
                           "password:", data->state.passwd,
                           sizeof(data->state.passwd)))
      {
        failf(data, "Bad password from password callback");
        return CURLE_BAD_PASSWORD_ENTERED;
      }
    }
  }
  /*
   * So if the URL was A://B/C,
   *   conn->protostr is A
   *   conn->gname is B
   *   conn->path is /C
   */

  /*************************************************************
   * Take care of proxy authentication stuff
@@ -1843,7 +1819,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
    }
    if(type) {
      char command;
      *type=0;
      *type=0;                     /* it was in the middle of the hostname */
      command = toupper(type[6]);
      switch(command) {
      case 'A': /* ASCII mode */
@@ -1911,86 +1887,6 @@ static CURLcode CreateConnection(struct SessionHandle *data,
    return CURLE_UNSUPPORTED_PROTOCOL;
  }

  /*************************************************************
   * .netrc scanning coming up
   *************************************************************/
  if(data->set.use_netrc) {
    if(Curl_parsenetrc(conn->hostname,
                       data->state.user,
                       data->state.passwd)) {
      infof(data, "Couldn't find host %s in the .netrc file, using defaults",
            conn->hostname);
    }
    else
      conn->bits.user_passwd = 1; /* enable user+password */

    /* weather we failed or not, we don't know which fields that were filled
       in anyway */
    if(!data->state.user[0])
      strcpy(data->state.user, CURL_DEFAULT_USER);
    if(!data->state.passwd[0])
      strcpy(data->state.passwd, CURL_DEFAULT_PASSWORD);
  }
  else if(!(conn->bits.user_passwd) &&
	  (conn->protocol & (PROT_FTP|PROT_HTTP)) ) {
    /* This is a FTP or HTTP URL, and we haven't got the user+password in
     * the extra parameter, we will now try to extract the possible
     * user+password pair in a string like:
     * ftp://user:password@ftp.my.site:8021/README */
    char *ptr=NULL; /* assign to remove possible warnings */
    if((ptr=strchr(conn->name, '@'))) {
      /* there's a user+password given here, to the left of the @ */

      data->state.user[0] =0;
      data->state.passwd[0]=0;

      if(*conn->name != ':') {
        /* the name is given, get user+password */
        sscanf(conn->name, "%127[^:@]:%127[^@]",
               data->state.user, data->state.passwd);
      }
      else
        /* no name given, get the password only */
        sscanf(conn->name+1, "%127[^@]", data->state.passwd);

      if(data->state.user[0]) {
        char *newname=curl_unescape(data->state.user, 0);
        if(strlen(newname) < sizeof(data->state.user)) {
          strcpy(data->state.user, newname);
        }
        /* if the new name is longer than accepted, then just use
           the unconverted name, it'll be wrong but what the heck */
        free(newname);
      }

      /* check for password, if no ask for one */
      if( !data->state.passwd[0] ) {
        if(!data->set.fpasswd ||
           data->set.fpasswd(data->set.passwd_client,
                             "password:", data->state.passwd,
                             sizeof(data->state.passwd))) {
          failf(data, "Bad password from password callback");
          return CURLE_BAD_PASSWORD_ENTERED;
        }
      }
      else {
        /* we have a password found in the URL, decode it! */
        char *newpasswd=curl_unescape(data->state.passwd, 0);
        if(strlen(newpasswd) < sizeof(data->state.passwd)) {
          strcpy(data->state.passwd, newpasswd);
        }
        free(newpasswd);
      }

      conn->name = ++ptr;
      conn->bits.user_passwd=TRUE; /* enable user+password */
    }
    else {
      strcpy(data->state.user, CURL_DEFAULT_USER);
      strcpy(data->state.passwd, CURL_DEFAULT_PASSWORD);
    }
  }

  /*************************************************************
   * Figure out the remote port number
   *
@@ -1999,29 +1895,32 @@ static CURLcode CreateConnection(struct SessionHandle *data,
   *
   * To be able to detect port number flawlessly, we must not confuse them
   * IPv6-specified addresses in the [0::1] style. (RFC2732)
   *
   * The conn->name is currently [user:passwd@]host[:port] where host could
   * be a hostname, IPv4 address or IPv6 address.
   *************************************************************/

  if((1 == sscanf(conn->name, "[%*39[0-9a-fA-F:.]%c", &endbracket)) &&
     (']' == endbracket)) {
    /* This is a (IPv6-style) specified IP-address. We support _any_
       IP within brackets to be really generic. */
    conn->name++; /* pass the starting bracket */
  tmp = strrchr(conn->name, ':');

    tmp = strchr(conn->name, ']');
    *tmp = 0; /* zero terminate */
  if (tmp) {
    char *rest;
    unsigned long port;

    tmp++; /* pass the ending bracket */
    if(':' != *tmp)
      tmp = NULL; /* no port number available */
  }
  else {
    /* traditional IPv4-style port-extracting */
    tmp = strchr(conn->name, ':');
    port=strtoul(tmp+1, &rest, 10);  /* Port number must be decimal */

    if (rest != (tmp+1) && *rest == '\0') {
      /* The colon really did have only digits after it,
       * so it is either a port number or a mistake */

      if (port > 0xffff) {   /* Single unix standard says port numbers are
                              * 16 bits long */
        failf(data, "Port number too large: %lu", port);
        return CURLE_URL_MALFORMAT;
      }

  if (tmp) {
    *tmp++ = '\0'; /* cut off the name there */
    conn->remote_port = atoi(tmp);
      *tmp = '\0'; /* cut off the name there */
      conn->remote_port = port;
    }
  }

  if(data->change.proxy) {
@@ -2075,6 +1974,138 @@ static CURLcode CreateConnection(struct SessionHandle *data,
    free(proxydup); /* free the duplicate pointer and not the modified */
  }

  /*************************************************************
   * Take care of user and password authentication stuff
   *************************************************************/

  /*
   * Inputs: data->set.userpwd   (CURLOPT_USERPWD)
   *         data->set.fpasswd   (CURLOPT_PASSWDFUNCTION)
   *         data->set.use_netrc (CURLOPT_NETRC)
   *         conn->hostname
   *         netrc file
   *         hard-coded defaults
   *
   * Outputs: (almost :- all currently undefined)
   *          conn->bits.user_passwd  - non-zero if non-default passwords exist
   *          conn->state.user        - non-zero length if defined
   *          conn->state.passwd      -   ditto
   *          conn->hostname          - remove user name and password
   */

  /* At this point, we're hoping all the other special cases have
   * been taken care of, so conn->hostname is at most
   *    [user[:password]]@]hostname
   *
   * We need somewhere to put the embedded details, so do that first.
   */

  data->state.user[0] =0;   /* to make everything well-defined */
  data->state.passwd[0]=0;
  
  if (conn->protocol & (PROT_FTP|PROT_HTTP)) {
    /* This is a FTP or HTTP URL, we will now try to extract the possible
     * user+password pair in a string like:
     * ftp://user:password@ftp.my.site:8021/README */
    char *ptr=strchr(conn->name, '@');
    char *userpass = conn->name;
    if(ptr != NULL) {
      /* there's a user+password given here, to the left of the @ */

      conn->name = conn->hostname = ++ptr;

      /* So the hostname is sane.  Only bother interpreting the
       * results if we could care.  It could still be wasted
       * work because it might be overtaken by the programmatically
       * set user/passwd, but doing that first adds more cases here :-(
       */

      if (data->set.use_netrc != CURL_NETRC_REQUIRED) {
        /* We could use the one in the URL */

        conn->bits.user_passwd = 1; /* enable user+password */

        if(*userpass != ':') {
          /* the name is given, get user+password */
          sscanf(userpass, "%127[^:@]:%127[^@]",
                 data->state.user, data->state.passwd);
        }
        else
          /* no name given, get the password only */
          sscanf(userpass, ":%127[^@]", data->state.passwd);

        if(data->state.user[0]) {
          char *newname=curl_unescape(data->state.user, 0);
          if(strlen(newname) < sizeof(data->state.user)) {
            strcpy(data->state.user, newname);
          }
          /* if the new name is longer than accepted, then just use
             the unconverted name, it'll be wrong but what the heck */
          free(newname);
        }
        if (data->state.passwd[0]) {
          /* we have a password found in the URL, decode it! */
          char *newpasswd=curl_unescape(data->state.passwd, 0);
          if(strlen(newpasswd) < sizeof(data->state.passwd)) {
            strcpy(data->state.passwd, newpasswd);
          }
          free(newpasswd);
        }
      }
    }
  }

  /* Programmatically set password:
   *   - always applies, if available
   *   - takes precedence over the values we just set above
   * so scribble it over the top.
   * User-supplied passwords are assumed not to need unescaping.
   *
   * user_password is set in "inherite initial knowledge' above,
   * so it doesn't have to be set in this block
   */
  if (data->set.userpwd != NULL) {
    if(*data->set.userpwd != ':') {
      /* the name is given, get user+password */
      sscanf(data->set.userpwd, "%127[^:]:%127[^\n]",
             data->state.user, data->state.passwd);
    }
    else
      /* no name given, get the password only */
      sscanf(data->set.userpwd+1, "%127[^\n]", data->state.passwd);
  }

  if (data->set.use_netrc != CURL_NETRC_IGNORED &&
      data->state.passwd[0] == '\0' ) {  /* need passwd */
    if(Curl_parsenetrc(conn->hostname,
                       data->state.user,
                       data->state.passwd)) {
      infof(data, "Couldn't find host %s in the .netrc file, using defaults",
            conn->hostname);
    } else
      conn->bits.user_passwd = 1; /* enable user+password */
  }

  /* if we have a user but no password, ask for one */
  if(conn->bits.user_passwd &&
     !data->state.passwd[0] ) {
    if(!data->set.fpasswd ||
      data->set.fpasswd(data->set.passwd_client,
                       "password:", data->state.passwd,
                           sizeof(data->state.passwd)))
      return CURLE_BAD_PASSWORD_ENTERED;
  }

  /* So we could have a password but no user; that's just too bad. */

  /* If our protocol needs a password and we have none, use the defaults */
  if ( (conn->protocol & (PROT_FTP|PROT_HTTP)) &&
       !conn->bits.user_passwd) {
    strcpy(data->state.user, CURL_DEFAULT_USER);
    strcpy(data->state.passwd, CURL_DEFAULT_PASSWORD);
    /* This is the default password, so DON'T set conn->bits.user_passwd */
  }

  /*************************************************************
   * Check the current list of connections to see if we can
   * re-use an already existing one or if we have to create a
+2 −1
Original line number Diff line number Diff line
@@ -650,7 +650,8 @@ struct UserDefined {
  bool no_body;
  bool set_port;
  bool upload;
  bool use_netrc;
  enum CURL_NETRC_OPTION
       use_netrc;        /* defined in include/curl.h */
  bool verbose;
  bool krb4;             /* kerberos4 connection requested */
  bool reuse_forbid;     /* forbidden to be reused, close after use */