|  | @@ -933,6 +933,47 @@ static int ntlm_challenge(auth_session *sess, int attempt,
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  #endif /* HAVE_NTLM */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +/* Username safety lookup table; "SF" for characters which are safe to
 | 
	
		
			
				|  |  | + * include in 2617-style username parameter values, else "NS" for
 | 
	
		
			
				|  |  | + * non-safe characters.  Determined from RFC7230§3.2.6 - everything in
 | 
	
		
			
				|  |  | + * qdtext EXCEPT obs-text (which is "non-ASCII") is treated as safe to
 | 
	
		
			
				|  |  | + * include in a quoted username. */
 | 
	
		
			
				|  |  | +#define SF 0
 | 
	
		
			
				|  |  | +#define NS 1
 | 
	
		
			
				|  |  | +static const signed char safe_username_table[256] = {
 | 
	
		
			
				|  |  | +/* 0xXX    x0      x2      x4      x6      x8      xA      xC      xE     */
 | 
	
		
			
				|  |  | +/*   0x */ NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS,
 | 
	
		
			
				|  |  | +/*   1x */ NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS,
 | 
	
		
			
				|  |  | +/*   2x */ NS, SF, NS, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF,
 | 
	
		
			
				|  |  | +/*   3x */ SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF,
 | 
	
		
			
				|  |  | +/*   4x */ SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF,
 | 
	
		
			
				|  |  | +/*   5x */ SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, NS, SF, SF, SF,
 | 
	
		
			
				|  |  | +/*   4x */ SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF,
 | 
	
		
			
				|  |  | +/*   7x */ SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, SF, NS,
 | 
	
		
			
				|  |  | +/*   8x */ NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS,
 | 
	
		
			
				|  |  | +/*   9x */ NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS,
 | 
	
		
			
				|  |  | +/*   Ax */ NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS,
 | 
	
		
			
				|  |  | +/*   Bx */ NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS,
 | 
	
		
			
				|  |  | +/*   Cx */ NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS,
 | 
	
		
			
				|  |  | +/*   Dx */ NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS,
 | 
	
		
			
				|  |  | +/*   Ex */ NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS,
 | 
	
		
			
				|  |  | +/*   Fx */ NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +#undef NS
 | 
	
		
			
				|  |  | +#undef SF
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* Returns non-zero if 'username' is unsafe to use without quoting. */
 | 
	
		
			
				|  |  | +static int unsafe_username(const char *username)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    const char *p;
 | 
	
		
			
				|  |  | +    int rv = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for (p = username; *p; p++)
 | 
	
		
			
				|  |  | +        rv |= safe_username_table[(const unsigned char)*p];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return rv;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /* Returns the H(username:realm:password) used in the Digest H(A1)
 | 
	
		
			
				|  |  |   * calculation. */
 | 
	
		
			
				|  |  |  static char *get_digest_h_urp(auth_session *sess, ne_buffer **errmsg,
 | 
	
	
		
			
				|  | @@ -959,37 +1000,16 @@ static char *get_digest_h_urp(auth_session *sess, ne_buffer **errmsg,
 | 
	
		
			
				|  |  |           * caller has indicated the username really is UTF-8; or
 | 
	
		
			
				|  |  |           * else b) the challenge is an error since the username
 | 
	
		
			
				|  |  |           * cannot be sent safely. */
 | 
	
		
			
				|  |  | -        char *esc = ne_strparam("UTF-8", NULL, (unsigned char *)sess->username);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        #ifdef WINSCP
 | 
	
		
			
				|  |  | -        if (esc && (parms->userhash == userhash_none || parms->handler->new_creds == NULL)) {
 | 
	
		
			
				|  |  | -            const char *p;
 | 
	
		
			
				|  |  | -            int ascii = 1;
 | 
	
		
			
				|  |  | -            for (p = sess->username; *p; p++) {
 | 
	
		
			
				|  |  | -                if (((*p & ~0x7f) != 0) || (!isprint(*p) && !isspace(*p))) {
 | 
	
		
			
				|  |  | -                    ascii = 0;
 | 
	
		
			
				|  |  | -                    break;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            if (ascii) {
 | 
	
		
			
				|  |  | -                ne_free(esc);
 | 
	
		
			
				|  |  | -                esc = NULL;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        #endif
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if (esc) {
 | 
	
		
			
				|  |  | +        if (unsafe_username(sess->username)) {
 | 
	
		
			
				|  |  |              if (parms->userhash == userhash_none
 | 
	
		
			
				|  |  |                  || parms->handler->new_creds == NULL) {
 | 
	
		
			
				|  |  | -                ne_free(esc);
 | 
	
		
			
				|  |  |                  challenge_error(errmsg, _("could not handle non-ASCII "
 | 
	
		
			
				|  |  |                                            "username in Digest challenge"));
 | 
	
		
			
				|  |  |                  ne__strzero(password, sizeof password);
 | 
	
		
			
				|  |  |                  return NULL;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | -            sess->username_star = esc;
 | 
	
		
			
				|  |  | -            NE_DEBUG(NE_DBG_HTTPAUTH, "auth: Using username* => %s\n", esc);
 | 
	
		
			
				|  |  | +            sess->username_star = ne_strparam("UTF-8", NULL, (unsigned char *)sess->username);
 | 
	
		
			
				|  |  | +            NE_DEBUG(NE_DBG_HTTPAUTH, "auth: Using username* => %s\n", sess->username_star);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 |