1 /* 2 * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org> 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining 5 * a copy of this software and associated documentation files (the 6 * "Software"), to deal in the Software without restriction, including 7 * without limitation the rights to use, copy, modify, merge, publish, 8 * distribute, sublicense, and/or sell copies of the Software, and to 9 * permit persons to whom the Software is furnished to do so, subject to 10 * the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be 13 * included in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 */ 24 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <stdint.h> 29 #include <errno.h> 30 31 #include "brssl.h" 32 33 /* see brssl.h */ 34 unsigned char * 35 read_file(const char *fname, size_t *len) 36 { 37 bvector vbuf = VEC_INIT; 38 FILE *f; 39 40 *len = 0; 41 f = fopen(fname, "rb"); 42 if (f == NULL) { 43 fprintf(stderr, 44 "ERROR: could not open file '%s' for reading\n", fname); 45 return NULL; 46 } 47 for (;;) { 48 unsigned char tmp[1024]; 49 size_t rlen; 50 51 rlen = fread(tmp, 1, sizeof tmp, f); 52 if (rlen == 0) { 53 unsigned char *buf; 54 55 if (ferror(f)) { 56 fprintf(stderr, 57 "ERROR: read error on file '%s'\n", 58 fname); 59 fclose(f); 60 return NULL; 61 } 62 buf = VEC_TOARRAY(vbuf); 63 *len = VEC_LEN(vbuf); 64 VEC_CLEAR(vbuf); 65 fclose(f); 66 return buf; 67 } 68 VEC_ADDMANY(vbuf, tmp, rlen); 69 } 70 } 71 72 /* see brssl.h */ 73 int 74 write_file(const char *fname, const void *data, size_t len) 75 { 76 FILE *f; 77 const unsigned char *buf; 78 79 f = fopen(fname, "wb"); 80 if (f == NULL) { 81 fprintf(stderr, 82 "ERROR: could not open file '%s' for reading\n", fname); 83 return -1; 84 } 85 buf = data; 86 while (len > 0) { 87 size_t wlen; 88 89 wlen = fwrite(buf, 1, len, f); 90 if (wlen == 0) { 91 fprintf(stderr, 92 "ERROR: could not write all bytes to '%s'\n", 93 fname); 94 fclose(f); 95 return -1; 96 } 97 buf += wlen; 98 len -= wlen; 99 } 100 if (ferror(f)) { 101 fprintf(stderr, "ERROR: write error on file '%s'\n", fname); 102 fclose(f); 103 return -1; 104 } 105 fclose(f); 106 return 0; 107 } 108 109 /* see brssl.h */ 110 int 111 looks_like_DER(const unsigned char *buf, size_t len) 112 { 113 int fb; 114 size_t dlen; 115 116 if (len < 2) { 117 return 0; 118 } 119 if (*buf ++ != 0x30) { 120 return 0; 121 } 122 fb = *buf ++; 123 len -= 2; 124 if (fb < 0x80) { 125 return (size_t)fb == len; 126 } else if (fb == 0x80) { 127 return 0; 128 } else { 129 fb -= 0x80; 130 if (len < (size_t)fb + 2) { 131 return 0; 132 } 133 len -= (size_t)fb; 134 dlen = 0; 135 while (fb -- > 0) { 136 if (dlen > (len >> 8)) { 137 return 0; 138 } 139 dlen = (dlen << 8) + (size_t)*buf ++; 140 } 141 return dlen == len; 142 } 143 } 144 145 static void 146 vblob_append(void *cc, const void *data, size_t len) 147 { 148 bvector *bv; 149 150 bv = cc; 151 VEC_ADDMANY(*bv, data, len); 152 } 153 154 /* see brssl.h */ 155 void 156 free_pem_object_contents(pem_object *po) 157 { 158 if (po != NULL) { 159 xfree(po->name); 160 xfree(po->data); 161 } 162 } 163 164 /* see brssl.h */ 165 pem_object * 166 decode_pem(const void *src, size_t len, size_t *num) 167 { 168 VECTOR(pem_object) pem_list = VEC_INIT; 169 br_pem_decoder_context pc; 170 pem_object po, *pos; 171 const unsigned char *buf; 172 bvector bv = VEC_INIT; 173 int inobj; 174 int extra_nl; 175 176 *num = 0; 177 br_pem_decoder_init(&pc); 178 buf = src; 179 inobj = 0; 180 po.name = NULL; 181 po.data = NULL; 182 po.data_len = 0; 183 extra_nl = 1; 184 while (len > 0) { 185 size_t tlen; 186 187 tlen = br_pem_decoder_push(&pc, buf, len); 188 buf += tlen; 189 len -= tlen; 190 switch (br_pem_decoder_event(&pc)) { 191 192 case BR_PEM_BEGIN_OBJ: 193 po.name = xstrdup(br_pem_decoder_name(&pc)); 194 br_pem_decoder_setdest(&pc, vblob_append, &bv); 195 inobj = 1; 196 break; 197 198 case BR_PEM_END_OBJ: 199 if (inobj) { 200 po.data = VEC_TOARRAY(bv); 201 po.data_len = VEC_LEN(bv); 202 VEC_ADD(pem_list, po); 203 VEC_CLEAR(bv); 204 po.name = NULL; 205 po.data = NULL; 206 po.data_len = 0; 207 inobj = 0; 208 } 209 break; 210 211 case BR_PEM_ERROR: 212 xfree(po.name); 213 VEC_CLEAR(bv); 214 fprintf(stderr, 215 "ERROR: invalid PEM encoding\n"); 216 VEC_CLEAREXT(pem_list, &free_pem_object_contents); 217 return NULL; 218 } 219 220 /* 221 * We add an extra newline at the end, in order to 222 * support PEM files that lack the newline on their last 223 * line (this is somwehat invalid, but PEM format is not 224 * standardised and such files do exist in the wild, so 225 * we'd better accept them). 226 */ 227 if (len == 0 && extra_nl) { 228 extra_nl = 0; 229 buf = (const unsigned char *)"\n"; 230 len = 1; 231 } 232 } 233 if (inobj) { 234 fprintf(stderr, "ERROR: unfinished PEM object\n"); 235 xfree(po.name); 236 VEC_CLEAR(bv); 237 VEC_CLEAREXT(pem_list, &free_pem_object_contents); 238 return NULL; 239 } 240 241 *num = VEC_LEN(pem_list); 242 VEC_ADD(pem_list, po); 243 pos = VEC_TOARRAY(pem_list); 244 VEC_CLEAR(pem_list); 245 return pos; 246 } 247 248 /* see brssl.h */ 249 br_x509_certificate * 250 read_certificates(const char *fname, size_t *num) 251 { 252 VECTOR(br_x509_certificate) cert_list = VEC_INIT; 253 unsigned char *buf; 254 size_t len; 255 pem_object *pos; 256 size_t u, num_pos; 257 br_x509_certificate *xcs; 258 br_x509_certificate dummy; 259 260 *num = 0; 261 262 /* 263 * TODO: reading the whole file is crude; we could parse them 264 * in a streamed fashion. But it does not matter much in practice. 265 */ 266 buf = read_file(fname, &len); 267 if (buf == NULL) { 268 return NULL; 269 } 270 271 /* 272 * Check for a DER-encoded certificate. 273 */ 274 if (looks_like_DER(buf, len)) { 275 xcs = xmalloc(2 * sizeof *xcs); 276 xcs[0].data = buf; 277 xcs[0].data_len = len; 278 xcs[1].data = NULL; 279 xcs[1].data_len = 0; 280 *num = 1; 281 return xcs; 282 } 283 284 pos = decode_pem(buf, len, &num_pos); 285 xfree(buf); 286 if (pos == NULL) { 287 return NULL; 288 } 289 for (u = 0; u < num_pos; u ++) { 290 if (eqstr(pos[u].name, "CERTIFICATE") 291 || eqstr(pos[u].name, "X509 CERTIFICATE")) 292 { 293 br_x509_certificate xc; 294 295 xc.data = pos[u].data; 296 xc.data_len = pos[u].data_len; 297 pos[u].data = NULL; 298 VEC_ADD(cert_list, xc); 299 } 300 } 301 for (u = 0; u < num_pos; u ++) { 302 free_pem_object_contents(&pos[u]); 303 } 304 xfree(pos); 305 306 if (VEC_LEN(cert_list) == 0) { 307 fprintf(stderr, "ERROR: no certificate in file '%s'\n", fname); 308 return NULL; 309 } 310 *num = VEC_LEN(cert_list); 311 dummy.data = NULL; 312 dummy.data_len = 0; 313 VEC_ADD(cert_list, dummy); 314 xcs = VEC_TOARRAY(cert_list); 315 VEC_CLEAR(cert_list); 316 return xcs; 317 } 318 319 /* see brssl.h */ 320 void 321 free_certificates(br_x509_certificate *certs, size_t num) 322 { 323 size_t u; 324 325 for (u = 0; u < num; u ++) { 326 xfree(certs[u].data); 327 } 328 xfree(certs); 329 } 330