1258945Sroberto/* 2280849Scy * Copyright (C) 2004, 2005, 2007, 2009 Internet Systems Consortium, Inc. ("ISC") 3258945Sroberto * Copyright (C) 1998-2001, 2003 Internet Software Consortium. 4258945Sroberto * 5258945Sroberto * Permission to use, copy, modify, and/or distribute this software for any 6258945Sroberto * purpose with or without fee is hereby granted, provided that the above 7258945Sroberto * copyright notice and this permission notice appear in all copies. 8258945Sroberto * 9258945Sroberto * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10258945Sroberto * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11258945Sroberto * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12258945Sroberto * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13258945Sroberto * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14258945Sroberto * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15258945Sroberto * PERFORMANCE OF THIS SOFTWARE. 16258945Sroberto */ 17258945Sroberto 18280849Scy/* $Id: base64.c,v 1.34 2009/10/21 23:48:05 tbox Exp $ */ 19258945Sroberto 20258945Sroberto/*! \file */ 21258945Sroberto 22258945Sroberto#include <config.h> 23258945Sroberto 24258945Sroberto#include <isc/base64.h> 25258945Sroberto#include <isc/buffer.h> 26258945Sroberto#include <isc/lex.h> 27258945Sroberto#include <isc/string.h> 28258945Sroberto#include <isc/util.h> 29258945Sroberto 30258945Sroberto#define RETERR(x) do { \ 31258945Sroberto isc_result_t _r = (x); \ 32258945Sroberto if (_r != ISC_R_SUCCESS) \ 33258945Sroberto return (_r); \ 34258945Sroberto } while (0) 35258945Sroberto 36258945Sroberto 37258945Sroberto/*@{*/ 38258945Sroberto/*! 39258945Sroberto * These static functions are also present in lib/dns/rdata.c. I'm not 40258945Sroberto * sure where they should go. -- bwelling 41258945Sroberto */ 42258945Srobertostatic isc_result_t 43258945Srobertostr_totext(const char *source, isc_buffer_t *target); 44258945Sroberto 45258945Srobertostatic isc_result_t 46258945Srobertomem_tobuffer(isc_buffer_t *target, void *base, unsigned int length); 47258945Sroberto 48258945Srobertostatic const char base64[] = 49258945Sroberto "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 50258945Sroberto/*@}*/ 51258945Sroberto 52258945Srobertoisc_result_t 53258945Srobertoisc_base64_totext(isc_region_t *source, int wordlength, 54258945Sroberto const char *wordbreak, isc_buffer_t *target) 55258945Sroberto{ 56258945Sroberto char buf[5]; 57258945Sroberto unsigned int loops = 0; 58258945Sroberto 59258945Sroberto if (wordlength < 4) 60258945Sroberto wordlength = 4; 61258945Sroberto 62258945Sroberto memset(buf, 0, sizeof(buf)); 63258945Sroberto while (source->length > 2) { 64258945Sroberto buf[0] = base64[(source->base[0]>>2)&0x3f]; 65258945Sroberto buf[1] = base64[((source->base[0]<<4)&0x30)| 66258945Sroberto ((source->base[1]>>4)&0x0f)]; 67258945Sroberto buf[2] = base64[((source->base[1]<<2)&0x3c)| 68258945Sroberto ((source->base[2]>>6)&0x03)]; 69258945Sroberto buf[3] = base64[source->base[2]&0x3f]; 70258945Sroberto RETERR(str_totext(buf, target)); 71258945Sroberto isc_region_consume(source, 3); 72258945Sroberto 73258945Sroberto loops++; 74258945Sroberto if (source->length != 0 && 75258945Sroberto (int)((loops + 1) * 4) >= wordlength) 76258945Sroberto { 77258945Sroberto loops = 0; 78258945Sroberto RETERR(str_totext(wordbreak, target)); 79258945Sroberto } 80258945Sroberto } 81258945Sroberto if (source->length == 2) { 82258945Sroberto buf[0] = base64[(source->base[0]>>2)&0x3f]; 83258945Sroberto buf[1] = base64[((source->base[0]<<4)&0x30)| 84258945Sroberto ((source->base[1]>>4)&0x0f)]; 85258945Sroberto buf[2] = base64[((source->base[1]<<2)&0x3c)]; 86258945Sroberto buf[3] = '='; 87258945Sroberto RETERR(str_totext(buf, target)); 88280849Scy isc_region_consume(source, 2); 89258945Sroberto } else if (source->length == 1) { 90258945Sroberto buf[0] = base64[(source->base[0]>>2)&0x3f]; 91258945Sroberto buf[1] = base64[((source->base[0]<<4)&0x30)]; 92258945Sroberto buf[2] = buf[3] = '='; 93258945Sroberto RETERR(str_totext(buf, target)); 94280849Scy isc_region_consume(source, 1); 95258945Sroberto } 96258945Sroberto return (ISC_R_SUCCESS); 97258945Sroberto} 98258945Sroberto 99258945Sroberto/*% 100258945Sroberto * State of a base64 decoding process in progress. 101258945Sroberto */ 102258945Srobertotypedef struct { 103258945Sroberto int length; /*%< Desired length of binary data or -1 */ 104258945Sroberto isc_buffer_t *target; /*%< Buffer for resulting binary data */ 105258945Sroberto int digits; /*%< Number of buffered base64 digits */ 106258945Sroberto isc_boolean_t seen_end; /*%< True if "=" end marker seen */ 107258945Sroberto int val[4]; 108258945Sroberto} base64_decode_ctx_t; 109258945Sroberto 110258945Srobertostatic inline void 111258945Srobertobase64_decode_init(base64_decode_ctx_t *ctx, int length, isc_buffer_t *target) 112258945Sroberto{ 113258945Sroberto ctx->digits = 0; 114258945Sroberto ctx->seen_end = ISC_FALSE; 115258945Sroberto ctx->length = length; 116258945Sroberto ctx->target = target; 117258945Sroberto} 118258945Sroberto 119258945Srobertostatic inline isc_result_t 120258945Srobertobase64_decode_char(base64_decode_ctx_t *ctx, int c) { 121258945Sroberto char *s; 122258945Sroberto 123258945Sroberto if (ctx->seen_end) 124258945Sroberto return (ISC_R_BADBASE64); 125258945Sroberto if ((s = strchr(base64, c)) == NULL) 126258945Sroberto return (ISC_R_BADBASE64); 127258945Sroberto ctx->val[ctx->digits++] = s - base64; 128258945Sroberto if (ctx->digits == 4) { 129258945Sroberto int n; 130258945Sroberto unsigned char buf[3]; 131258945Sroberto if (ctx->val[0] == 64 || ctx->val[1] == 64) 132258945Sroberto return (ISC_R_BADBASE64); 133258945Sroberto if (ctx->val[2] == 64 && ctx->val[3] != 64) 134258945Sroberto return (ISC_R_BADBASE64); 135258945Sroberto /* 136258945Sroberto * Check that bits that should be zero are. 137258945Sroberto */ 138258945Sroberto if (ctx->val[2] == 64 && (ctx->val[1] & 0xf) != 0) 139258945Sroberto return (ISC_R_BADBASE64); 140258945Sroberto /* 141258945Sroberto * We don't need to test for ctx->val[2] != 64 as 142258945Sroberto * the bottom two bits of 64 are zero. 143258945Sroberto */ 144258945Sroberto if (ctx->val[3] == 64 && (ctx->val[2] & 0x3) != 0) 145258945Sroberto return (ISC_R_BADBASE64); 146258945Sroberto n = (ctx->val[2] == 64) ? 1 : 147258945Sroberto (ctx->val[3] == 64) ? 2 : 3; 148258945Sroberto if (n != 3) { 149258945Sroberto ctx->seen_end = ISC_TRUE; 150258945Sroberto if (ctx->val[2] == 64) 151258945Sroberto ctx->val[2] = 0; 152258945Sroberto if (ctx->val[3] == 64) 153258945Sroberto ctx->val[3] = 0; 154258945Sroberto } 155258945Sroberto buf[0] = (ctx->val[0]<<2)|(ctx->val[1]>>4); 156258945Sroberto buf[1] = (ctx->val[1]<<4)|(ctx->val[2]>>2); 157258945Sroberto buf[2] = (ctx->val[2]<<6)|(ctx->val[3]); 158258945Sroberto RETERR(mem_tobuffer(ctx->target, buf, n)); 159258945Sroberto if (ctx->length >= 0) { 160258945Sroberto if (n > ctx->length) 161258945Sroberto return (ISC_R_BADBASE64); 162258945Sroberto else 163258945Sroberto ctx->length -= n; 164258945Sroberto } 165258945Sroberto ctx->digits = 0; 166258945Sroberto } 167258945Sroberto return (ISC_R_SUCCESS); 168258945Sroberto} 169258945Sroberto 170258945Srobertostatic inline isc_result_t 171258945Srobertobase64_decode_finish(base64_decode_ctx_t *ctx) { 172258945Sroberto if (ctx->length > 0) 173258945Sroberto return (ISC_R_UNEXPECTEDEND); 174258945Sroberto if (ctx->digits != 0) 175258945Sroberto return (ISC_R_BADBASE64); 176258945Sroberto return (ISC_R_SUCCESS); 177258945Sroberto} 178258945Sroberto 179258945Srobertoisc_result_t 180258945Srobertoisc_base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) { 181258945Sroberto base64_decode_ctx_t ctx; 182258945Sroberto isc_textregion_t *tr; 183258945Sroberto isc_token_t token; 184258945Sroberto isc_boolean_t eol; 185258945Sroberto 186258945Sroberto base64_decode_init(&ctx, length, target); 187258945Sroberto 188258945Sroberto while (!ctx.seen_end && (ctx.length != 0)) { 189258945Sroberto unsigned int i; 190258945Sroberto 191258945Sroberto if (length > 0) 192258945Sroberto eol = ISC_FALSE; 193258945Sroberto else 194258945Sroberto eol = ISC_TRUE; 195258945Sroberto RETERR(isc_lex_getmastertoken(lexer, &token, 196258945Sroberto isc_tokentype_string, eol)); 197258945Sroberto if (token.type != isc_tokentype_string) 198258945Sroberto break; 199258945Sroberto tr = &token.value.as_textregion; 200258945Sroberto for (i = 0; i < tr->length; i++) 201258945Sroberto RETERR(base64_decode_char(&ctx, tr->base[i])); 202258945Sroberto } 203258945Sroberto if (ctx.length < 0 && !ctx.seen_end) 204258945Sroberto isc_lex_ungettoken(lexer, &token); 205258945Sroberto RETERR(base64_decode_finish(&ctx)); 206258945Sroberto return (ISC_R_SUCCESS); 207258945Sroberto} 208258945Sroberto 209258945Srobertoisc_result_t 210258945Srobertoisc_base64_decodestring(const char *cstr, isc_buffer_t *target) { 211258945Sroberto base64_decode_ctx_t ctx; 212258945Sroberto 213258945Sroberto base64_decode_init(&ctx, -1, target); 214258945Sroberto for (;;) { 215258945Sroberto int c = *cstr++; 216258945Sroberto if (c == '\0') 217258945Sroberto break; 218258945Sroberto if (c == ' ' || c == '\t' || c == '\n' || c== '\r') 219258945Sroberto continue; 220258945Sroberto RETERR(base64_decode_char(&ctx, c)); 221258945Sroberto } 222280849Scy RETERR(base64_decode_finish(&ctx)); 223258945Sroberto return (ISC_R_SUCCESS); 224258945Sroberto} 225258945Sroberto 226258945Srobertostatic isc_result_t 227258945Srobertostr_totext(const char *source, isc_buffer_t *target) { 228258945Sroberto unsigned int l; 229258945Sroberto isc_region_t region; 230258945Sroberto 231258945Sroberto isc_buffer_availableregion(target, ®ion); 232258945Sroberto l = strlen(source); 233258945Sroberto 234258945Sroberto if (l > region.length) 235258945Sroberto return (ISC_R_NOSPACE); 236258945Sroberto 237258945Sroberto memcpy(region.base, source, l); 238258945Sroberto isc_buffer_add(target, l); 239258945Sroberto return (ISC_R_SUCCESS); 240258945Sroberto} 241258945Sroberto 242258945Srobertostatic isc_result_t 243258945Srobertomem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) { 244258945Sroberto isc_region_t tr; 245258945Sroberto 246258945Sroberto isc_buffer_availableregion(target, &tr); 247258945Sroberto if (length > tr.length) 248258945Sroberto return (ISC_R_NOSPACE); 249258945Sroberto memcpy(tr.base, base, length); 250258945Sroberto isc_buffer_add(target, length); 251258945Sroberto return (ISC_R_SUCCESS); 252258945Sroberto} 253