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