From: rjk@greenend.org.uk <> Date: Mon, 31 Dec 2007 11:46:17 +0000 (+0000) Subject: Confirmation strings are now invariant under URL-encoding. They are still X-Git-Tag: 3.0~121 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/commitdiff_plain/86e3aea7faeeed443729e174fc1339f08b72086a Confirmation strings are now invariant under URL-encoding. They are still base64-encoded but using a slightly different set of symbols to MIME. --- diff --git a/lib/base64.c b/lib/base64.c index c657cde..7ba4730 100644 --- a/lib/base64.c +++ b/lib/base64.c @@ -33,17 +33,31 @@ #include "vector.h" static const char mime_base64_table[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; /** @brief Convert MIME base64 * @param s base64 data * @param nsp Where to store length of converted data + * @param table Table of characters to use * @return Decoded data * * See RFC * 2045 s6.8. */ char *mime_base64(const char *s, size_t *nsp) { + return generic_base64(s, nsp, mime_base64_table); +} + +/** @brief Convert base64 + * @param s base64 data + * @param nsp Where to store length of converted data + * @return Decoded data + * + * @p table should consist of 65 characters. The first 64 will be used to + * represents the 64 digits and the 65th will be used as padding at the end + * (i.e. the role of '=' in RFC2045 base64). + */ +char *generic_base64(const char *s, size_t *nsp, const char *table) { struct dynstr d; const char *t; int b[4], n, c; @@ -51,15 +65,15 @@ char *mime_base64(const char *s, size_t *nsp) { dynstr_init(&d); n = 0; while((c = (unsigned char)*s++)) { - if((t = strchr(mime_base64_table, c))) { - b[n++] = t - mime_base64_table; + if((t = strchr(table, c))) { + b[n++] = t - table; if(n == 4) { dynstr_append(&d, (b[0] << 2) + (b[1] >> 4)); dynstr_append(&d, (b[1] << 4) + (b[2] >> 2)); dynstr_append(&d, (b[2] << 6) + b[3]); n = 0; } - } else if(c == '=') { + } else if(c == table[64]) { if(n >= 2) { dynstr_append(&d, (b[0] << 2) + (b[1] >> 4)); if(n == 3) @@ -74,7 +88,7 @@ char *mime_base64(const char *s, size_t *nsp) { return d.vec; } -/** @brief Convert a binary string to base64 +/** @brief Convert a binary string to MIME base64 * @param s Bytes to convert * @param ns Number of bytes to convert * @return Encoded data @@ -85,34 +99,50 @@ char *mime_base64(const char *s, size_t *nsp) { * 2045 s6.8. */ char *mime_to_base64(const uint8_t *s, size_t ns) { + return generic_to_base64(s, ns, mime_base64_table); +} + +/** @brief Convert a binary string to base64 + * @param s Bytes to convert + * @param ns Number of bytes to convert + * @param table Table of characters to use + * @return Encoded data + * + * This function does not attempt to split up lines. + * + * @p table should consist of 65 characters. The first 64 will be used to + * represents the 64 digits and the 65th will be used as padding at the end + * (i.e. the role of '=' in RFC2045 base64). + */ +char *generic_to_base64(const uint8_t *s, size_t ns, const char *table) { struct dynstr d[1]; dynstr_init(d); while(ns >= 3) { /* Input bytes with output bits: AAAAAABB BBBBCCCC CCDDDDDD */ /* Output bytes with input bits: 000000 001111 111122 222222 */ - dynstr_append(d, mime_base64_table[s[0] >> 2]); - dynstr_append(d, mime_base64_table[((s[0] & 3) << 4) + dynstr_append(d, table[s[0] >> 2]); + dynstr_append(d, table[((s[0] & 3) << 4) + (s[1] >> 4)]); - dynstr_append(d, mime_base64_table[((s[1] & 15) << 2) + dynstr_append(d, table[((s[1] & 15) << 2) + (s[2] >> 6)]); - dynstr_append(d, mime_base64_table[s[2] & 63]); + dynstr_append(d, table[s[2] & 63]); ns -= 3; s += 3; } if(ns > 0) { - dynstr_append(d, mime_base64_table[s[0] >> 2]); + dynstr_append(d, table[s[0] >> 2]); switch(ns) { case 1: - dynstr_append(d, mime_base64_table[(s[0] & 3) << 4]); - dynstr_append(d, '='); - dynstr_append(d, '='); + dynstr_append(d, table[(s[0] & 3) << 4]); + dynstr_append(d, table[64]); + dynstr_append(d, table[64]); break; case 2: - dynstr_append(d, mime_base64_table[((s[0] & 3) << 4) + dynstr_append(d, table[((s[0] & 3) << 4) + (s[1] >> 4)]); - dynstr_append(d, mime_base64_table[(s[1] & 15) << 2]); - dynstr_append(d, '='); + dynstr_append(d, table[(s[1] & 15) << 2]); + dynstr_append(d, table[64]); break; } } diff --git a/lib/base64.h b/lib/base64.h index a2c20e6..0beb534 100644 --- a/lib/base64.h +++ b/lib/base64.h @@ -25,7 +25,9 @@ #define BASE64_H char *mime_base64(const char *s, size_t *nsp); +char *generic_base64(const char *s, size_t *nsp, const char *table); char *mime_to_base64(const uint8_t *s, size_t ns); +char *generic_to_base64(const uint8_t *s, size_t ns, const char *table); #endif /* BASE64_H */ diff --git a/server/server.c b/server/server.c index a7fad01..1e7c22f 100644 --- a/server/server.c +++ b/server/server.c @@ -1154,6 +1154,16 @@ static int c_users(struct conn *c, return 1; /* completed */ } +/** @brief Base64 mapping table for confirmation strings + * + * This is used with generic_to_base64() and generic_base64(). We cannot use + * the MIME table as that contains '+' and '=' which get quoted when + * URL-encoding. (The CGI still does the URL encoding but it is desirable to + * avoid it being necessary.) + */ +static const char confirm_base64_table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/.*"; + static int c_register(struct conn *c, char **vec, int attribute((unused)) nvec) { @@ -1166,7 +1176,8 @@ static int c_register(struct conn *c, buf = xmalloc_noptr(bufsize); offset = byte_snprintf(buf, bufsize, "%s;", vec[0]); gcry_randomize(buf + offset, CONFIRM_SIZE, GCRY_STRONG_RANDOM); - cs = mime_to_base64((uint8_t *)buf, offset + CONFIRM_SIZE); + cs = generic_to_base64((uint8_t *)buf, offset + CONFIRM_SIZE, + confirm_base64_table); if(trackdb_adduser(vec[0], vec[1], config->default_rights, vec[2], cs)) sink_writes(ev_writer_sink(c->w), "550 Cannot create user\n"); else @@ -1187,7 +1198,7 @@ static int c_confirm(struct conn *c, sink_writes(ev_writer_sink(c->w), "530 Authentication failure\n"); return 1; } - if(!(user = mime_base64(vec[0], &nuser)) + if(!(user = generic_base64(vec[0], &nuser, confirm_base64_table)) || !(sep = memchr(user, ';', nuser))) { sink_writes(ev_writer_sink(c->w), "550 Malformed confirmation string\n"); return 1;