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