1/* 2 * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 1998-2001, 2003 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 * PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18/* $Id: base64.c,v 1.32 2007/06/19 23:47:17 tbox Exp $ */ 19 20/*! \file */ 21 22#include <config.h> 23 24#include <isc/base64.h> 25#include <isc/buffer.h> 26#include <isc/lex.h> 27#include <isc/string.h> 28#include <isc/util.h> 29 30#define RETERR(x) do { \ 31 isc_result_t _r = (x); \ 32 if (_r != ISC_R_SUCCESS) \ 33 return (_r); \ 34 } while (0) 35 36 37/*@{*/ 38/*! 39 * These static functions are also present in lib/dns/rdata.c. I'm not 40 * sure where they should go. -- bwelling 41 */ 42static isc_result_t 43str_totext(const char *source, isc_buffer_t *target); 44 45static isc_result_t 46mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length); 47 48static const char base64[] = 49 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 50/*@}*/ 51 52isc_result_t 53isc_base64_totext(isc_region_t *source, int wordlength, 54 const char *wordbreak, isc_buffer_t *target) 55{ 56 char buf[5]; 57 unsigned int loops = 0; 58 59 if (wordlength < 4) 60 wordlength = 4; 61 62 memset(buf, 0, sizeof(buf)); 63 while (source->length > 2) { 64 buf[0] = base64[(source->base[0]>>2)&0x3f]; 65 buf[1] = base64[((source->base[0]<<4)&0x30)| 66 ((source->base[1]>>4)&0x0f)]; 67 buf[2] = base64[((source->base[1]<<2)&0x3c)| 68 ((source->base[2]>>6)&0x03)]; 69 buf[3] = base64[source->base[2]&0x3f]; 70 RETERR(str_totext(buf, target)); 71 isc_region_consume(source, 3); 72 73 loops++; 74 if (source->length != 0 && 75 (int)((loops + 1) * 4) >= wordlength) 76 { 77 loops = 0; 78 RETERR(str_totext(wordbreak, target)); 79 } 80 } 81 if (source->length == 2) { 82 buf[0] = base64[(source->base[0]>>2)&0x3f]; 83 buf[1] = base64[((source->base[0]<<4)&0x30)| 84 ((source->base[1]>>4)&0x0f)]; 85 buf[2] = base64[((source->base[1]<<2)&0x3c)]; 86 buf[3] = '='; 87 RETERR(str_totext(buf, target)); 88 } else if (source->length == 1) { 89 buf[0] = base64[(source->base[0]>>2)&0x3f]; 90 buf[1] = base64[((source->base[0]<<4)&0x30)]; 91 buf[2] = buf[3] = '='; 92 RETERR(str_totext(buf, target)); 93 } 94 return (ISC_R_SUCCESS); 95} 96 97/*% 98 * State of a base64 decoding process in progress. 99 */ 100typedef struct { 101 int length; /*%< Desired length of binary data or -1 */ 102 isc_buffer_t *target; /*%< Buffer for resulting binary data */ 103 int digits; /*%< Number of buffered base64 digits */ 104 isc_boolean_t seen_end; /*%< True if "=" end marker seen */ 105 int val[4]; 106} base64_decode_ctx_t; 107 108static inline void 109base64_decode_init(base64_decode_ctx_t *ctx, int length, isc_buffer_t *target) 110{ 111 ctx->digits = 0; 112 ctx->seen_end = ISC_FALSE; 113 ctx->length = length; 114 ctx->target = target; 115} 116 117static inline isc_result_t 118base64_decode_char(base64_decode_ctx_t *ctx, int c) { 119 char *s; 120 121 if (ctx->seen_end) 122 return (ISC_R_BADBASE64); 123 if ((s = strchr(base64, c)) == NULL) 124 return (ISC_R_BADBASE64); 125 ctx->val[ctx->digits++] = s - base64; 126 if (ctx->digits == 4) { 127 int n; 128 unsigned char buf[3]; 129 if (ctx->val[0] == 64 || ctx->val[1] == 64) 130 return (ISC_R_BADBASE64); 131 if (ctx->val[2] == 64 && ctx->val[3] != 64) 132 return (ISC_R_BADBASE64); 133 /* 134 * Check that bits that should be zero are. 135 */ 136 if (ctx->val[2] == 64 && (ctx->val[1] & 0xf) != 0) 137 return (ISC_R_BADBASE64); 138 /* 139 * We don't need to test for ctx->val[2] != 64 as 140 * the bottom two bits of 64 are zero. 141 */ 142 if (ctx->val[3] == 64 && (ctx->val[2] & 0x3) != 0) 143 return (ISC_R_BADBASE64); 144 n = (ctx->val[2] == 64) ? 1 : 145 (ctx->val[3] == 64) ? 2 : 3; 146 if (n != 3) { 147 ctx->seen_end = ISC_TRUE; 148 if (ctx->val[2] == 64) 149 ctx->val[2] = 0; 150 if (ctx->val[3] == 64) 151 ctx->val[3] = 0; 152 } 153 buf[0] = (ctx->val[0]<<2)|(ctx->val[1]>>4); 154 buf[1] = (ctx->val[1]<<4)|(ctx->val[2]>>2); 155 buf[2] = (ctx->val[2]<<6)|(ctx->val[3]); 156 RETERR(mem_tobuffer(ctx->target, buf, n)); 157 if (ctx->length >= 0) { 158 if (n > ctx->length) 159 return (ISC_R_BADBASE64); 160 else 161 ctx->length -= n; 162 } 163 ctx->digits = 0; 164 } 165 return (ISC_R_SUCCESS); 166} 167 168static inline isc_result_t 169base64_decode_finish(base64_decode_ctx_t *ctx) { 170 if (ctx->length > 0) 171 return (ISC_R_UNEXPECTEDEND); 172 if (ctx->digits != 0) 173 return (ISC_R_BADBASE64); 174 return (ISC_R_SUCCESS); 175} 176 177isc_result_t 178isc_base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) { 179 base64_decode_ctx_t ctx; 180 isc_textregion_t *tr; 181 isc_token_t token; 182 isc_boolean_t eol; 183 184 base64_decode_init(&ctx, length, target); 185 186 while (!ctx.seen_end && (ctx.length != 0)) { 187 unsigned int i; 188 189 if (length > 0) 190 eol = ISC_FALSE; 191 else 192 eol = ISC_TRUE; 193 RETERR(isc_lex_getmastertoken(lexer, &token, 194 isc_tokentype_string, eol)); 195 if (token.type != isc_tokentype_string) 196 break; 197 tr = &token.value.as_textregion; 198 for (i = 0; i < tr->length; i++) 199 RETERR(base64_decode_char(&ctx, tr->base[i])); 200 } 201 if (ctx.length < 0 && !ctx.seen_end) 202 isc_lex_ungettoken(lexer, &token); 203 RETERR(base64_decode_finish(&ctx)); 204 return (ISC_R_SUCCESS); 205} 206 207isc_result_t 208isc_base64_decodestring(const char *cstr, isc_buffer_t *target) { 209 base64_decode_ctx_t ctx; 210 211 base64_decode_init(&ctx, -1, target); 212 for (;;) { 213 int c = *cstr++; 214 if (c == '\0') 215 break; 216 if (c == ' ' || c == '\t' || c == '\n' || c== '\r') 217 continue; 218 RETERR(base64_decode_char(&ctx, c)); 219 } 220 RETERR(base64_decode_finish(&ctx)); 221 return (ISC_R_SUCCESS); 222} 223 224static isc_result_t 225str_totext(const char *source, isc_buffer_t *target) { 226 unsigned int l; 227 isc_region_t region; 228 229 isc_buffer_availableregion(target, ®ion); 230 l = strlen(source); 231 232 if (l > region.length) 233 return (ISC_R_NOSPACE); 234 235 memcpy(region.base, source, l); 236 isc_buffer_add(target, l); 237 return (ISC_R_SUCCESS); 238} 239 240static isc_result_t 241mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) { 242 isc_region_t tr; 243 244 isc_buffer_availableregion(target, &tr); 245 if (length > tr.length) 246 return (ISC_R_NOSPACE); 247 memcpy(tr.base, base, length); 248 isc_buffer_add(target, length); 249 return (ISC_R_SUCCESS); 250} 251