diff --git a/CHANGES b/CHANGES index 4cd31eafed..aabc534302 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,9 @@ + 433. [func] isc_base64_decodestring() now accepts newlines + within the base64 data. This makes it possible + to break up the key data in a "trusted-keys" + statement into multiple lines. [RT #284] + 432. [func] Added refresh/retry jitter. This is currently hard-coded to be no more than 20% of the SOA provided time or 10 minutes, whichever is less. diff --git a/lib/isc/base64.c b/lib/isc/base64.c index d595c171f3..07c78a1f10 100644 --- a/lib/isc/base64.c +++ b/lib/isc/base64.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: base64.c,v 1.18 2000/08/01 01:29:14 tale Exp $ */ +/* $Id: base64.c,v 1.19 2000/09/08 00:34:21 gson Exp $ */ #include @@ -94,20 +94,86 @@ isc_base64_totext(isc_region_t *source, int wordlength, return (ISC_R_SUCCESS); } +/* + * State of a base64 decoding process in progress. + */ +typedef struct { + int length; /* Desired length of binary data or -1 */ + isc_buffer_t *target; /* Buffer for resulting binary data */ + int digits; /* Number of buffered base64 digits */ + isc_boolean_t seen_end; /* True if "=" end marker seen */ + int val[4]; +} base64_decode_ctx_t; + +static inline void +base64_decode_init(base64_decode_ctx_t *ctx, int length, isc_buffer_t *target) +{ + ctx->digits = 0; + ctx->seen_end = ISC_FALSE; + ctx->length = length; + ctx->target = target; +} + +static inline isc_result_t +base64_decode_char(base64_decode_ctx_t *ctx, int c) { + char *s; + + if (ctx->seen_end) + return (ISC_R_BADBASE64); + if ((s = strchr(base64, c)) == NULL) + return (ISC_R_BADBASE64); + ctx->val[ctx->digits++] = s - base64; + if (ctx->digits == 4) { + int n; + unsigned char buf[3]; + if (ctx->val[0] == 64 || ctx->val[1] == 64) + return (ISC_R_BADBASE64); + if (ctx->val[2] == 64 && ctx->val[3] != 64) + return (ISC_R_BADBASE64); + n = (ctx->val[2] == 64) ? 1 : + (ctx->val[3] == 64) ? 2 : 3; + if (n != 3) { + ctx->seen_end = ISC_TRUE; + if (ctx->val[2] == 64) + ctx->val[2] = 0; + if (ctx->val[3] == 64) + ctx->val[3] = 0; + } + buf[0] = (ctx->val[0]<<2)|(ctx->val[1]>>4); + buf[1] = (ctx->val[1]<<4)|(ctx->val[2]>>2); + buf[2] = (ctx->val[2]<<6)|(ctx->val[3]); + RETERR(mem_tobuffer(ctx->target, buf, n)); + if (ctx->length >= 0) { + if (n > ctx->length) + return (ISC_R_BADBASE64); + else + ctx->length -= n; + } + ctx->digits = 0; + } + return (ISC_R_SUCCESS); +} + +static inline isc_result_t +base64_decode_finish(base64_decode_ctx_t *ctx) { + if (ctx->length > 0) + return (ISC_R_UNEXPECTEDEND); + if (ctx->digits != 0) + return (ISC_R_BADBASE64); + return (ISC_R_SUCCESS); +} + isc_result_t isc_base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) { - int digits = 0; + base64_decode_ctx_t ctx; isc_textregion_t *tr; - int val[4]; - unsigned char buf[3]; - int seen_end = 0; - unsigned int i; isc_token_t token; - char *s; - int n; + base64_decode_init(&ctx, length, target); + + while (!ctx.seen_end && (ctx.length != 0)) { + unsigned int i; - while (!seen_end && (length != 0)) { if (length > 0) RETERR(gettoken(lexer, &token, isc_tokentype_string, ISC_FALSE)); @@ -117,82 +183,34 @@ isc_base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) { if (token.type != isc_tokentype_string) break; tr = &token.value.as_textregion; - for (i = 0 ;i < tr->length; i++) { - if (seen_end) - return (ISC_R_BADBASE64); - if ((s = strchr(base64, tr->base[i])) == NULL) - return (ISC_R_BADBASE64); - val[digits++] = s - base64; - if (digits == 4) { - if (val[0] == 64 || val[1] == 64) - return (ISC_R_BADBASE64); - if (val[2] == 64 && val[3] != 64) - return (ISC_R_BADBASE64); - n = (val[2] == 64) ? 1 : - (val[3] == 64) ? 2 : 3; - if (n != 3) { - seen_end = 1; - if (val[2] == 64) - val[2] = 0; - if (val[3] == 64) - val[3] = 0; - } - buf[0] = (val[0]<<2)|(val[1]>>4); - buf[1] = (val[1]<<4)|(val[2]>>2); - buf[2] = (val[2]<<6)|(val[3]); - RETERR(mem_tobuffer(target, buf, n)); - if (length >= 0) { - if (n > length) - return (ISC_R_BADBASE64); - else - length -= n; - } - digits = 0; - } - } + for (i = 0 ;i < tr->length; i++) + RETERR(base64_decode_char(&ctx, tr->base[i])); } - if (length < 0 && !seen_end) + if (ctx.length < 0 && !ctx.seen_end) isc_lex_ungettoken(lexer, &token); - if (length > 0) - return (ISC_R_UNEXPECTEDEND); - if (digits != 0) - return (ISC_R_BADBASE64); + RETERR(base64_decode_finish(&ctx)); return (ISC_R_SUCCESS); } isc_result_t isc_base64_decodestring(isc_mem_t *mctx, char *cstr, isc_buffer_t *target) { - isc_result_t result; - isc_buffer_t source; - isc_lex_t *lex = NULL; - isc_boolean_t isopen = ISC_FALSE; + base64_decode_ctx_t ctx; - REQUIRE(mctx != NULL); - REQUIRE(cstr != NULL); - REQUIRE(ISC_BUFFER_VALID(target)); + UNUSED(mctx); - isc_buffer_init(&source, cstr, strlen(cstr)); - isc_buffer_add(&source, strlen(cstr)); - - result = isc_lex_create(mctx, 256, &lex); - - if (result == ISC_R_SUCCESS) - result = isc_lex_openbuffer(lex, &source); - - if (result == ISC_R_SUCCESS) { - isopen = ISC_TRUE; - result = isc_base64_tobuffer(lex, target, -1); + base64_decode_init(&ctx, -1, target); + for (;;) { + int c = *cstr++; + if (c == '\0') + break; + if (c == ' ' || c == '\t' || c == '\n' || c== '\r') + continue; + RETERR(base64_decode_char(&ctx, c)); } - - if (isopen) - (void)isc_lex_close(lex); - if (lex != NULL) - isc_lex_destroy(&lex); - - return (result); + RETERR(base64_decode_finish(&ctx)); + return (ISC_R_SUCCESS); } - static isc_result_t str_totext(const char *source, isc_buffer_t *target) { unsigned int l;