1135446Strhodes/* 2262706Serwin * Copyright (C) 2004, 2005, 2007, 2009, 2013, 2014 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 1998-2001, 2003 Internet Software Consortium. 4135446Strhodes * 5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18234010Sdougb/* $Id: base64.c,v 1.34 2009/10/21 23:48:05 tbox Exp $ */ 19135446Strhodes 20170222Sdougb/*! \file */ 21170222Sdougb 22135446Strhodes#include <config.h> 23135446Strhodes 24135446Strhodes#include <isc/base64.h> 25135446Strhodes#include <isc/buffer.h> 26135446Strhodes#include <isc/lex.h> 27135446Strhodes#include <isc/string.h> 28135446Strhodes#include <isc/util.h> 29135446Strhodes 30135446Strhodes#define RETERR(x) do { \ 31135446Strhodes isc_result_t _r = (x); \ 32135446Strhodes if (_r != ISC_R_SUCCESS) \ 33135446Strhodes return (_r); \ 34135446Strhodes } while (0) 35135446Strhodes 36135446Strhodes 37170222Sdougb/*@{*/ 38170222Sdougb/*! 39135446Strhodes * These static functions are also present in lib/dns/rdata.c. I'm not 40135446Strhodes * sure where they should go. -- bwelling 41135446Strhodes */ 42135446Strhodesstatic isc_result_t 43135446Strhodesstr_totext(const char *source, isc_buffer_t *target); 44135446Strhodes 45135446Strhodesstatic isc_result_t 46135446Strhodesmem_tobuffer(isc_buffer_t *target, void *base, unsigned int length); 47135446Strhodes 48135446Strhodesstatic const char base64[] = 49135446Strhodes "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 50170222Sdougb/*@}*/ 51135446Strhodes 52135446Strhodesisc_result_t 53135446Strhodesisc_base64_totext(isc_region_t *source, int wordlength, 54135446Strhodes const char *wordbreak, isc_buffer_t *target) 55135446Strhodes{ 56135446Strhodes char buf[5]; 57135446Strhodes unsigned int loops = 0; 58135446Strhodes 59135446Strhodes if (wordlength < 4) 60135446Strhodes wordlength = 4; 61135446Strhodes 62135446Strhodes memset(buf, 0, sizeof(buf)); 63135446Strhodes while (source->length > 2) { 64135446Strhodes buf[0] = base64[(source->base[0]>>2)&0x3f]; 65135446Strhodes buf[1] = base64[((source->base[0]<<4)&0x30)| 66135446Strhodes ((source->base[1]>>4)&0x0f)]; 67135446Strhodes buf[2] = base64[((source->base[1]<<2)&0x3c)| 68135446Strhodes ((source->base[2]>>6)&0x03)]; 69135446Strhodes buf[3] = base64[source->base[2]&0x3f]; 70135446Strhodes RETERR(str_totext(buf, target)); 71135446Strhodes isc_region_consume(source, 3); 72135446Strhodes 73135446Strhodes loops++; 74135446Strhodes if (source->length != 0 && 75135446Strhodes (int)((loops + 1) * 4) >= wordlength) 76135446Strhodes { 77135446Strhodes loops = 0; 78135446Strhodes RETERR(str_totext(wordbreak, target)); 79135446Strhodes } 80135446Strhodes } 81135446Strhodes if (source->length == 2) { 82135446Strhodes buf[0] = base64[(source->base[0]>>2)&0x3f]; 83135446Strhodes buf[1] = base64[((source->base[0]<<4)&0x30)| 84135446Strhodes ((source->base[1]>>4)&0x0f)]; 85135446Strhodes buf[2] = base64[((source->base[1]<<2)&0x3c)]; 86135446Strhodes buf[3] = '='; 87135446Strhodes RETERR(str_totext(buf, target)); 88204619Sdougb isc_region_consume(source, 2); 89135446Strhodes } else if (source->length == 1) { 90135446Strhodes buf[0] = base64[(source->base[0]>>2)&0x3f]; 91135446Strhodes buf[1] = base64[((source->base[0]<<4)&0x30)]; 92135446Strhodes buf[2] = buf[3] = '='; 93135446Strhodes RETERR(str_totext(buf, target)); 94204619Sdougb isc_region_consume(source, 1); 95135446Strhodes } 96135446Strhodes return (ISC_R_SUCCESS); 97135446Strhodes} 98135446Strhodes 99170222Sdougb/*% 100135446Strhodes * State of a base64 decoding process in progress. 101135446Strhodes */ 102135446Strhodestypedef struct { 103170222Sdougb int length; /*%< Desired length of binary data or -1 */ 104170222Sdougb isc_buffer_t *target; /*%< Buffer for resulting binary data */ 105170222Sdougb int digits; /*%< Number of buffered base64 digits */ 106170222Sdougb isc_boolean_t seen_end; /*%< True if "=" end marker seen */ 107135446Strhodes int val[4]; 108135446Strhodes} base64_decode_ctx_t; 109135446Strhodes 110135446Strhodesstatic inline void 111135446Strhodesbase64_decode_init(base64_decode_ctx_t *ctx, int length, isc_buffer_t *target) 112135446Strhodes{ 113135446Strhodes ctx->digits = 0; 114135446Strhodes ctx->seen_end = ISC_FALSE; 115135446Strhodes ctx->length = length; 116135446Strhodes ctx->target = target; 117135446Strhodes} 118135446Strhodes 119135446Strhodesstatic inline isc_result_t 120135446Strhodesbase64_decode_char(base64_decode_ctx_t *ctx, int c) { 121135446Strhodes char *s; 122135446Strhodes 123135446Strhodes if (ctx->seen_end) 124135446Strhodes return (ISC_R_BADBASE64); 125135446Strhodes if ((s = strchr(base64, c)) == NULL) 126135446Strhodes return (ISC_R_BADBASE64); 127262706Serwin ctx->val[ctx->digits++] = (int)(s - base64); 128135446Strhodes if (ctx->digits == 4) { 129135446Strhodes int n; 130135446Strhodes unsigned char buf[3]; 131135446Strhodes if (ctx->val[0] == 64 || ctx->val[1] == 64) 132135446Strhodes return (ISC_R_BADBASE64); 133135446Strhodes if (ctx->val[2] == 64 && ctx->val[3] != 64) 134135446Strhodes return (ISC_R_BADBASE64); 135135446Strhodes /* 136135446Strhodes * Check that bits that should be zero are. 137135446Strhodes */ 138135446Strhodes if (ctx->val[2] == 64 && (ctx->val[1] & 0xf) != 0) 139135446Strhodes return (ISC_R_BADBASE64); 140135446Strhodes /* 141135446Strhodes * We don't need to test for ctx->val[2] != 64 as 142135446Strhodes * the bottom two bits of 64 are zero. 143135446Strhodes */ 144135446Strhodes if (ctx->val[3] == 64 && (ctx->val[2] & 0x3) != 0) 145135446Strhodes return (ISC_R_BADBASE64); 146135446Strhodes n = (ctx->val[2] == 64) ? 1 : 147135446Strhodes (ctx->val[3] == 64) ? 2 : 3; 148135446Strhodes if (n != 3) { 149135446Strhodes ctx->seen_end = ISC_TRUE; 150135446Strhodes if (ctx->val[2] == 64) 151135446Strhodes ctx->val[2] = 0; 152135446Strhodes if (ctx->val[3] == 64) 153135446Strhodes ctx->val[3] = 0; 154135446Strhodes } 155135446Strhodes buf[0] = (ctx->val[0]<<2)|(ctx->val[1]>>4); 156135446Strhodes buf[1] = (ctx->val[1]<<4)|(ctx->val[2]>>2); 157135446Strhodes buf[2] = (ctx->val[2]<<6)|(ctx->val[3]); 158135446Strhodes RETERR(mem_tobuffer(ctx->target, buf, n)); 159135446Strhodes if (ctx->length >= 0) { 160135446Strhodes if (n > ctx->length) 161135446Strhodes return (ISC_R_BADBASE64); 162135446Strhodes else 163135446Strhodes ctx->length -= n; 164135446Strhodes } 165135446Strhodes ctx->digits = 0; 166135446Strhodes } 167135446Strhodes return (ISC_R_SUCCESS); 168135446Strhodes} 169135446Strhodes 170135446Strhodesstatic inline isc_result_t 171135446Strhodesbase64_decode_finish(base64_decode_ctx_t *ctx) { 172135446Strhodes if (ctx->length > 0) 173135446Strhodes return (ISC_R_UNEXPECTEDEND); 174135446Strhodes if (ctx->digits != 0) 175135446Strhodes return (ISC_R_BADBASE64); 176135446Strhodes return (ISC_R_SUCCESS); 177135446Strhodes} 178135446Strhodes 179135446Strhodesisc_result_t 180135446Strhodesisc_base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) { 181135446Strhodes base64_decode_ctx_t ctx; 182135446Strhodes isc_textregion_t *tr; 183135446Strhodes isc_token_t token; 184135446Strhodes isc_boolean_t eol; 185135446Strhodes 186135446Strhodes base64_decode_init(&ctx, length, target); 187135446Strhodes 188135446Strhodes while (!ctx.seen_end && (ctx.length != 0)) { 189135446Strhodes unsigned int i; 190135446Strhodes 191135446Strhodes if (length > 0) 192135446Strhodes eol = ISC_FALSE; 193135446Strhodes else 194135446Strhodes eol = ISC_TRUE; 195135446Strhodes RETERR(isc_lex_getmastertoken(lexer, &token, 196135446Strhodes isc_tokentype_string, eol)); 197135446Strhodes if (token.type != isc_tokentype_string) 198135446Strhodes break; 199135446Strhodes tr = &token.value.as_textregion; 200135446Strhodes for (i = 0; i < tr->length; i++) 201135446Strhodes RETERR(base64_decode_char(&ctx, tr->base[i])); 202135446Strhodes } 203135446Strhodes if (ctx.length < 0 && !ctx.seen_end) 204135446Strhodes isc_lex_ungettoken(lexer, &token); 205135446Strhodes RETERR(base64_decode_finish(&ctx)); 206135446Strhodes return (ISC_R_SUCCESS); 207135446Strhodes} 208135446Strhodes 209135446Strhodesisc_result_t 210135446Strhodesisc_base64_decodestring(const char *cstr, isc_buffer_t *target) { 211135446Strhodes base64_decode_ctx_t ctx; 212135446Strhodes 213135446Strhodes base64_decode_init(&ctx, -1, target); 214135446Strhodes for (;;) { 215135446Strhodes int c = *cstr++; 216135446Strhodes if (c == '\0') 217135446Strhodes break; 218135446Strhodes if (c == ' ' || c == '\t' || c == '\n' || c== '\r') 219135446Strhodes continue; 220135446Strhodes RETERR(base64_decode_char(&ctx, c)); 221135446Strhodes } 222204619Sdougb RETERR(base64_decode_finish(&ctx)); 223135446Strhodes return (ISC_R_SUCCESS); 224135446Strhodes} 225135446Strhodes 226135446Strhodesstatic isc_result_t 227135446Strhodesstr_totext(const char *source, isc_buffer_t *target) { 228135446Strhodes unsigned int l; 229135446Strhodes isc_region_t region; 230135446Strhodes 231135446Strhodes isc_buffer_availableregion(target, ®ion); 232135446Strhodes l = strlen(source); 233135446Strhodes 234135446Strhodes if (l > region.length) 235135446Strhodes return (ISC_R_NOSPACE); 236135446Strhodes 237262706Serwin memmove(region.base, source, l); 238135446Strhodes isc_buffer_add(target, l); 239135446Strhodes return (ISC_R_SUCCESS); 240135446Strhodes} 241135446Strhodes 242135446Strhodesstatic isc_result_t 243135446Strhodesmem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) { 244135446Strhodes isc_region_t tr; 245135446Strhodes 246135446Strhodes isc_buffer_availableregion(target, &tr); 247135446Strhodes if (length > tr.length) 248135446Strhodes return (ISC_R_NOSPACE); 249262706Serwin memmove(tr.base, base, length); 250135446Strhodes isc_buffer_add(target, length); 251135446Strhodes return (ISC_R_SUCCESS); 252135446Strhodes} 253