long /* return serial on success, -1 on failure */ smime_get_signer_info(const char* signed_entity, int info_ix, /* 0 = first signer */ char** issuer) /* DN of the issuer */ { int serial = -1; PKCS7* p7 = NULL; STACK_OF(PKCS7_SIGNER_INFO)* sigs = NULL; PKCS7_SIGNER_INFO* si; if (!signed_entity || !issuer) GOTO_ERR("NULL arg(s)"); *issuer = NULL; if (!(p7 = get_pkcs7_from_pem(signed_entity))) goto err; if (!(sigs=PKCS7_get_signer_info(p7))) GOTO_ERR("13 no sigs? (PKCS7_get_signer_info)"); if (info_ix >= sk_PKCS7_SIGNER_INFO_num(sigs)) GOTO_ERR("No more signers. info_ix too large."); if (!(si=sk_PKCS7_SIGNER_INFO_value(sigs,info_ix))) GOTO_ERR("NULL signer info"); *issuer = X509_NAME_oneline(si->issuer_and_serial->issuer, NULL,0); serial = ASN1_INTEGER_get(si->issuer_and_serial->serial); err: if (p7) PKCS7_free(p7); return serial; }
char * otp_gen(int len, otp_type_t type) { long int number = 0; int i, fd = 0; char *otp = NULL; char *otp_type = NULL; otp_type = select_otp_type(type); if ((otp = (char *)malloc((len+1) * sizeof (char))) == NULL) return NULL; if ((fd = open("/dev/urandom", O_RDONLY)) < 0) GOTO_ERR(pam_data.pamh, "E: can't open /dev/urandom."); for (i=0; i<len; i++) { if (read(fd, &number, sizeof number) < 0) GOTO_ERR(pam_data.pamh, "E: can't read /dev/urandom."); otp[i] = otp_type[number % strlen(otp_type)]; } otp[len] = '\0'; close(fd); return otp; err: if (otp != NULL) free(otp); if (fd > 0) close(fd); return NULL; }
int /* return size of data, -1 on failure */ decrypt(X509* x509, EVP_PKEY* pkey, const char* enc_entity, char** data_out) { char* p; char buf[4096]; int i,n; BIO* wbio = NULL; BIO* p7bio = NULL; PKCS7 *p7 = NULL; if (data_out) *data_out = NULL; if (!x509 || !pkey || !enc_entity || !data_out) GOTO_ERR("NULL arg(s)"); LOG_PRINT("decrypt: get_pkcs7_from_pem"); if (!(p7 = get_pkcs7_from_pem(enc_entity))) goto err; /* Decrypt the symmetric key with private key and obtain symmetric * cipher stream (BIO). The cert is needed here to look up one of * possibly multiple recipient infos present in PKCS7 object. Issuer * and serial number must match (these two fields form unique ID for * cert). Actual public key part of the X509 cert is not used for * anything here. */ LOG_PRINT("decrypt: dataDecode"); if (!(p7bio=PKCS7_dataDecode(p7,pkey,NULL/*detached*/,x509))) GOTO_ERR("12 no recipient matches cert or private key could not decrypt, i.e. wrong key (PKCS7_dataDecode)"); LOG_PRINT("decrypt: ready to pump"); /* Pump data from p7bio to decrypt symmetric cipher */ if (!(wbio = BIO_new(BIO_s_mem()))) GOTO_ERR("no memory?"); for (;;) { if ((i=BIO_read(p7bio,buf,sizeof(buf))) <= 0) break; BIO_write(wbio,buf,i); } BIO_flush(wbio); BIO_free_all(p7bio); p7bio = NULL; PKCS7_free(p7); p7 = NULL; LOG_PRINT("decrypt: pump done"); /* Return data (this should now be easier because we just freed * some memory) */ n = get_written_BIO_data(wbio, data_out); BIO_free_all(wbio); return n; err: if (p7) PKCS7_free(p7); if (wbio) BIO_free_all(wbio); if (p7bio) BIO_free_all(p7bio); return -1; }
int /* returns -1 if error, 0 if verfy fail, and 1 if verify OK */ verify_cert(X509* ca_cert, X509* cert) { EVP_PKEY* pkey = NULL; if (!ca_cert || !cert) GOTO_ERR("NULL arg(s)"); if (!(pkey=X509_get_pubkey(ca_cert))) GOTO_ERR("no memory?"); return X509_verify(cert, pkey); err: return -1; }
static PKCS7* get_pkcs7_from_pem(const char* enc_entity) { const char* p; char* wrapped_enc_entity = NULL; BIO* rbio = NULL; PKCS7* p7 = NULL; /* Check if encrypted entity is composed of raw data or if it has some * headers. In the latter case, just skip the headers. Headers are * separated from data by an empty line (hence sequence CRLF CRLF).*/ if (p = strstr(enc_entity, CRLF CRLF)) { enc_entity = p+4; } else if (p = strstr(enc_entity, LF LF)) { enc_entity = p+2; } else if (p = strstr(enc_entity, CR CR)) { enc_entity = p+2; } /* Make sure the pem markers are there */ LOG_PRINT("get_pkcs7_from_pem: wrapping in pem markers"); if (!(wrapped_enc_entity = wrap_in_pem_markers(enc_entity, "PKCS7"))) GOTO_ERR("no memory?"); LOG_PRINT("get_pkcs7_from_pem: wrapped."); /* Set up BIO so encrypted/signed data can be read from pem file */ if (!(rbio = set_read_BIO_from_buf(wrapped_enc_entity, -1))) goto err; LOG_PRINT("get_pkcs7_from_pem: ready to read PKCS7 bio..."); /* Load the PKCS7 object from a pem file to internal representation. * this reads all of the data file. */ if (!(p7=PEM_read_bio_PKCS7(rbio,NULL/*&x*/,NULL/*callback*/,NULL/*arg*/))) GOTO_ERR("11 corrupt PEM PKCS7 file? (PEM_read_bio_PKCS7)"); LOG_PRINT("get_pkcs7_from_pem: bio read"); BIO_free_all(rbio); Free(wrapped_enc_entity); return p7; err: if (rbio) BIO_free_all(rbio); if (wrapped_enc_entity) Free(wrapped_enc_entity); return NULL; }
/* Called by: mk_multipart */ char* mime_mk_multipart(const char* text, const char* file1, int len1, const char* type1, const char* name1, const char* file2, int len2, const char* type2, const char* name2, const char* file3, int len3, const char* type3, const char* name3) { char* b; /* Concatenate all components into one message. This type of stuff * is sooo ugly in C. I'm missing perl. */ if (!(b = strdup("Content-type: multipart/mixed; boundary=" SEP CRLF CRLF "--" SEP CRLF "Content-type: text/plain" CRLF "Content-transfer-encoding: 8bit" CRLF CRLF))) GOTO_ERR("no memory?"); if (!(b = concat(b, text))) goto err; if (!(b = concat(b, CRLF "--" SEP))) goto err; if (!(b = attach(b, file1, len1, type1, name1))) goto err; if (!(b = attach(b, file2, len2, type2, name2))) goto err; if (!(b = attach(b, file3, len3, type3, name3))) goto err; if (!(b = concat(b, "--" CRLF))) goto err; return b; err: return NULL; }
/* Called by: clear_sign, extract_certificate, extract_request, main, open_private_key, sign */ char* mime_canon(const char* s) { char* d; char* p; int len; len = strlen(s); p = d = (char*)OPENSSL_malloc(len + len); /* Reserve spaces for CR's to be inserted. */ if (!d) GOTO_ERR("no memory?"); /* Scan and copy */ for (; *s; s++) { if (s[0] != '\015' && s[0] != '\012') *(p++) = *s; /* pass thru */ else { if (s[0] == '\015' && s[1] == '\012') s++; /* already CRLF */ *(p++) = '\015'; *(p++) = '\012'; } } *(p++) = '\0'; /* Shrink the buffer back to actual size (not very likely to fail) */ return (char*)OPENSSL_realloc(d, (p-d)); err: return NULL; }
/* For this case, I will malloc the return strings */ static int get_signed_seq2string(PKCS7_SIGNER_INFO *si, char **str1, char **str2) { ASN1_TYPE *so; #if 0 if (signed_seq2string_nid == -1) signed_seq2string_nid= OBJ_create("1.9.9999","OID_example","Our example OID"); /* To retrieve */ so=PKCS7_get_signed_attribute(si,signed_seq2string_nid); if (so && (so->type == V_ASN1_SEQUENCE)) { ASN1_CTX c; ASN1_STRING *s; long length; ASN1_OCTET_STRING *os1,*os2; s=so->value.sequence; c.p=ASN1_STRING_data(s); c.max=c.p+ASN1_STRING_length(s); if (!asn1_GetSequence(&c,&length)) GOTO_ERR("") err; /* Length is the length of the seqence */ c.q=c.p; if ((os1=d2i_ASN1_OCTET_STRING(NULL,&c.p,c.slen)) == NULL) GOTO_ERR(""); c.slen-=(c.p-c.q); c.q=c.p; if ((os2=d2i_ASN1_OCTET_STRING(NULL,&c.p,c.slen)) == NULL) GOTO_ERR(""); c.slen-=(c.p-c.q); if (!asn1_Finish(&c)) GOTO_ERR("") err; *str1=Malloc(os1->length+1); *str2=Malloc(os2->length+1); memcpy(*str1,os1->data,os1->length); memcpy(*str2,os2->data,os2->length); (*str1)[os1->length]='\0'; (*str2)[os2->length]='\0'; ASN1_OCTET_STRING_free(os1); ASN1_OCTET_STRING_free(os2); return(1); } #endif err: return(0); }
/* Called by: main x3 */ char* mime_base64_entity(const char* data, int len, const char* type) { /*int n;*/ char* b64; char* b; if (!(b = strdup("Content-type: "))) GOTO_ERR("no memory?"); if (!(b = concat(b, type))) goto err; if (!(b = concat(b, CRLF CRLF))) goto err; /*n =*/ smime_base64(1, data, len, &b64); if (!b64) GOTO_ERR("no memory?"); if (!(b = concat(b, b64))) goto err; return b; err: return NULL; }
/* Called by: attach x9, encrypt1, get_req_attr x3, mime_base64_entity x3, mime_mk_multipart x3, mime_raw_entity x3, sign, smime_mk_multipart_signed x4 */ char* concat(char* b, const char* s) { if (!(b = (char*)OPENSSL_realloc(b, strlen(b)+strlen(s)+1))) GOTO_ERR("no memory?"); strcat(b,s); return b; err: return NULL; }
/* Called by: get_req_attr */ char* concatmem(char* b, const char* s, int len) { int lb = strlen(b); if (!(b = (char*)OPENSSL_realloc(b, lb+len+1))) GOTO_ERR("no memory?"); memcpy(b+lb, s, len); b[lb+len] = '\0'; return b; err: return NULL; }
/* Called by: main */ char* mime_raw_entity(const char* text, const char* type) { char* b; if (!(b = strdup("Content-type: "))) GOTO_ERR("no memory?"); if (!(b = concat(b, type))) goto err; if (!(b = concat(b, CRLF CRLF))) goto err; if (!(b = concat(b, text))) goto err; return b; err: return NULL; }
int /* returns -1 if error, 0 if verfy fail, and 1 if verify OK */ smime_verify_cert(const char* ca_cert_pem, const char* cert_pem) { X509* ca_cert = NULL; X509* cert = NULL; int ret = -1; if (!ca_cert_pem || !cert_pem) GOTO_ERR("NULL arg(s)"); if (!(ca_cert = extract_certificate(ca_cert_pem))) goto err; if (!(cert = extract_certificate(cert_pem))) goto err; ret = verify_cert(ca_cert, cert); err: if (ca_cert) X509_free(ca_cert); if (cert) X509_free(cert); return ret; }
/* Called by: clear_sign */ char* /* returns smime encoded clear sig blob, or NULL if error */ smime_mk_multipart_signed(const char* mime_entity, const char* sig_entity) { char* b; if (!(b = strdup("Content-type: multipart/signed; protocol=\"application/x-pkcs7-signature\"; micalg=sha1; boundary=" SIG CRLF CRLF "--" SIG CRLF))) GOTO_ERR("no memory?"); if (!(b = concat(b, mime_entity))) goto err; if (!(b = concat(b, CRLF "--" SIG CRLF "Content-Type: application/x-pkcs7-signature; name=\"smime.p7s\"" CRLF "Content-Transfer-Encoding: base64" CRLF "Content-Disposition: attachment; filename=\"smime.p7s\"" CRLF CRLF))) goto err; if (!(b = concat(b, sig_entity))) goto err; if (!(b = concat(b, CRLF "--" SIG "--" CRLF))) goto err; return b; err: return NULL; }
int /* return size of data, -1 on failure */ smime_decrypt(const char* privkey, const char* passwd, const char* enc_entity, char** data_out) { int n = -1; EVP_PKEY *pkey = NULL; X509 *x509 = NULL; if (data_out) *data_out = NULL; if (!privkey || !passwd || !enc_entity || !data_out) GOTO_ERR("NULL arg(s)"); if (!(pkey = open_private_key(privkey, passwd))) goto err; if (!(x509 = extract_certificate(privkey))) goto err; n = decrypt(x509, pkey, enc_entity, data_out); err: if (pkey) EVP_PKEY_free(pkey); if (x509) X509_free(x509); return n; }
/* Called by: get_pkcs7_from_pem */ char* wrap_in_pem_markers(const char* b, char* algo) { char* bb; int n; int algolen = strlen(algo); n = strlen(b); if (!(bb = (char*)OPENSSL_malloc(5+6+algolen+6+ n +5+4+algolen+6 +1 +1))) GOTO_ERR("no memory?"); strcpy(bb, "-----BEGIN "); strcat(bb, algo); strcat(bb, "-----\n"); strcat(bb, b); if (b[n-1] != '\012' && b[n-1] != '\015') /* supply \n if missing */ strcat(bb, "\n"); strcat(bb, "-----END "); strcat(bb, algo); strcat(bb, "-----\n"); n = strlen(bb); return bb; err: return NULL; }
char* /* returns contents of the signed message, NULL if error */ smime_verify_signature(const char* pubkey, const char* sig_entity, /* signed entity or just the sigature */ const char* detached_data, /* possibly NULL */ int detached_data_len) { X509* x509 = NULL; PKCS7* p7 = NULL; STACK_OF(PKCS7_SIGNER_INFO)* sigs = NULL; X509_STORE* certs=NULL; BIO* detached = NULL; BIO* p7bio = NULL; BIO* wbio = NULL; char* data = NULL; char buf[4096]; int i,x; if (!sig_entity || !pubkey) GOTO_ERR("NULL arg(s)"); if (!(p7 = get_pkcs7_from_pem(sig_entity))) goto err; /* Hmm, if its clear signed, we already provided the detached sig, but * if its one sig blob, may be PKCS7_get_detached() provides BIO connected * to the detached part. Go figure. */ if (detached_data && detached_data_len) { if (!(detached = set_read_BIO_from_buf(detached_data, detached_data_len))) goto err; } else { if (!PKCS7_get_detached(p7)) GOTO_ERR("15 cant extract signed data from signed entity (PKCS7_get_detached)"); } if (!(p7bio=PKCS7_dataInit(p7,detached))) GOTO_ERR("PKCS7_dataInit"); if (!(wbio = BIO_new(BIO_s_mem()))) GOTO_ERR("no memory?"); /* We now have to 'read' from p7bio to calculate message digest(s). * I also take the opportunity to save the signed data. */ for (;;) { i = BIO_read(p7bio,buf,sizeof(buf)); if (i <= 0) break; BIO_write(wbio, buf, i); } if (get_written_BIO_data(wbio, &data)==-1) goto err; BIO_free_all(wbio); wbio = NULL; /* We can now verify signatures */ if (!(sigs=PKCS7_get_signer_info(p7))) GOTO_ERR("13 no sigs? (PKCS7_get_signer_info)"); /* Ok, first we need to, for each subject entry, see if we can verify */ for (i=0; i<sk_PKCS7_SIGNER_INFO_num(sigs); i++) { X509_STORE_CTX cert_ctx; PKCS7_SIGNER_INFO *si; ASN1_UTCTIME *tm; char *str1,*str2; si=sk_PKCS7_SIGNER_INFO_value(sigs,i); /* The bio is needed here only to lookup the message digest context * which presumably now contains the message digest. It will not be * read, and hence its good for any number of iterations. This is so * because MD bios pass the data right thru so they can be stacked * to calculate multiple message digests simultaneously. Clever, eh? */ #if 0 /* *** this is currently broken and thus disabled. --Sampo */ /* verifies by looking up the certificate from certs database, * verifying the validity of the certificate, and finally * validity of the signature */ x=PKCS7_dataVerify(certs, &cert_ctx, p7bio, p7, si); #else /* just verify the signature, given that we already have certificate * candidate (see crypto/pk7_doit.c around line 675) */ if (!(x509 = extract_certificate(pubkey))) goto err; x=PKCS7_signatureVerify(p7bio, p7, si, x509); #endif if (x <= 0) GOTO_ERR("14 sig verify failed"); #if 0 if ((tm=get_signed_time(si)) != NULL) { //fprintf(stderr,"Signed time:"); //ASN1_UTCTIME_print(bio_out,tm); ASN1_UTCTIME_free(tm); //BIO_printf(bio_out,"\n"); } #endif #if 0 if (get_signed_seq2string(si,&str1,&str2)) { fprintf(stderr,"String 1 is %s\n",str1); fprintf(stderr,"String 2 is %s\n",str2); } #endif } BIO_free_all(p7bio); PKCS7_free(p7); X509_STORE_free(certs); return data; /* return the signed plain text */ err: if (wbio) BIO_free_all(wbio); if (p7bio) BIO_free_all(p7bio); if (p7) PKCS7_free(p7); if (certs) X509_STORE_free(certs); if (data) Free(data); return NULL; }
int /* returns number of parts, -1 on error */ mime_split_multipart(const char* entity, int max_parts, /* how big following arrays are */ char* parts[], /* NULL --> just count parts */ int lengths[]) { char separator[256]; int nparts = -1; int sep_len, len; char* p; char* pp; char* ppp; char* start = NULL; /* start of current sub entity */ if (!entity || (parts && !lengths)) GOTO_ERR("NULL arg(s)"); if (!(p = strstr(entity, "Content-type: multipart/"))) GOTO_ERR("16 No `Content-type: multipart/...' header found"); if (!(p = strstr(p, "boundary="))) GOTO_ERR("16 Badly formed multipart header. Didn't find `boundary='."); p+=9; /* strlen("boundary=") */ sep_len = strcspn(p, "\015\012 ;"); if (sep_len <= 0) GOTO_ERR("16 No boundary separator?"); if (sep_len >= (int)sizeof(separator)) GOTO_ERR("16 Too long boundary separator. Only 255 chars allowed."); separator[0] = separator[1] = '-'; memcpy(separator+2, p, sep_len); sep_len+=2; separator[sep_len] = '\0'; p+=sep_len; while ((p = strstr(p, separator))) { ppp = pp = p; p+=sep_len; if (*p != '\015' && *p != '\012' && *p != '-') continue; /* False positive: separator appeared as line prefix */ if (pp[-1] == '\012') pp--; /* walk back and eat the CRLF */ if (pp[-1] == '\015') pp--; if (pp == ppp) continue; /* False positive: separator in middle of line */ if (start && parts && nparts < max_parts) { len = lengths[nparts] = pp-start; if (!(parts[nparts] = (char*)OPENSSL_malloc(len+1))) GOTO_ERR("no memory?"); memcpy(parts[nparts], start, len); parts[nparts][len] = '\0'; /* Gratuitous nul termination */ } nparts++; if (*p == '\015') p++; /* Skip CRLF */ if (*p == '\012') p++; /* This is really mandatory */ start = p; } return nparts; /* Called by: */ err: if (parts) { /* free what we have allocated so far */ for (sep_len = 0; sep_len < nparts; sep_len++) if (parts[sep_len]) OPENSSL_free(parts[sep_len]); } return -1; }