/*1* Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan2* (Royal Institute of Technology, Stockholm, Sweden).3* All rights reserved.4*5* Portions Copyright (c) 2009 Apple Inc. All rights reserved.6*7* Redistribution and use in source and binary forms, with or without8* modification, are permitted provided that the following conditions9* are met:10*11* 1. Redistributions of source code must retain the above copyright12* notice, this list of conditions and the following disclaimer.13*14* 2. Redistributions in binary form must reproduce the above copyright15* notice, this list of conditions and the following disclaimer in the16* documentation and/or other materials provided with the distribution.17*18* 3. Neither the name of the Institute nor the names of its contributors19* may be used to endorse or promote products derived from this software20* without specific prior written permission.21*22* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND23* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE24* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE25* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE26* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL27* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS28* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)29* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT30* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY31* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF32* SUCH DAMAGE.33*/3435#include "hx_locl.h"3637/**38* @page page_keyset Certificate store operations39*40* Type of certificates store:41* - MEMORY42* In memory based format. Doesnt support storing.43* - FILE44* FILE supports raw DER certicates and PEM certicates. When PEM is45* used the file can contain may certificates and match private46* keys. Support storing the certificates. DER format only supports47* on certificate and no private key.48* - PEM-FILE49* Same as FILE, defaulting to PEM encoded certificates.50* - PEM-FILE51* Same as FILE, defaulting to DER encoded certificates.52* - PKCS1153* - PKCS1254* - DIR55* - KEYCHAIN56* Apple Mac OS X KeyChain backed keychain object.57*58* See the library functions here: @ref hx509_keyset59*/6061struct hx509_certs_data {62unsigned int ref;63struct hx509_keyset_ops *ops;64void *ops_data;65};6667static struct hx509_keyset_ops *68_hx509_ks_type(hx509_context context, const char *type)69{70int i;7172for (i = 0; i < context->ks_num_ops; i++)73if (strcasecmp(type, context->ks_ops[i]->name) == 0)74return context->ks_ops[i];7576return NULL;77}7879void80_hx509_ks_register(hx509_context context, struct hx509_keyset_ops *ops)81{82struct hx509_keyset_ops **val;8384if (_hx509_ks_type(context, ops->name))85return;8687val = realloc(context->ks_ops,88(context->ks_num_ops + 1) * sizeof(context->ks_ops[0]));89if (val == NULL)90return;91val[context->ks_num_ops] = ops;92context->ks_ops = val;93context->ks_num_ops++;94}9596/**97* Open or creates a new hx509 certificate store.98*99* @param context A hx509 context100* @param name name of the store, format is TYPE:type-specific-string,101* if NULL is used the MEMORY store is used.102* @param flags list of flags:103* - HX509_CERTS_CREATE create a new keystore of the specific TYPE.104* - HX509_CERTS_UNPROTECT_ALL fails if any private key failed to be extracted.105* @param lock a lock that unlocks the certificates store, use NULL to106* select no password/certifictes/prompt lock (see @ref page_lock).107* @param certs return pointer, free with hx509_certs_free().108*109* @ingroup hx509_keyset110*/111112int113hx509_certs_init(hx509_context context,114const char *name, int flags,115hx509_lock lock, hx509_certs *certs)116{117struct hx509_keyset_ops *ops;118const char *residue;119hx509_certs c;120char *type;121int ret;122123*certs = NULL;124125residue = strchr(name, ':');126if (residue) {127type = malloc(residue - name + 1);128if (type)129strlcpy(type, name, residue - name + 1);130residue++;131if (residue[0] == '\0')132residue = NULL;133} else {134type = strdup("MEMORY");135residue = name;136}137if (type == NULL) {138hx509_clear_error_string(context);139return ENOMEM;140}141142ops = _hx509_ks_type(context, type);143if (ops == NULL) {144hx509_set_error_string(context, 0, ENOENT,145"Keyset type %s is not supported", type);146free(type);147return ENOENT;148}149free(type);150c = calloc(1, sizeof(*c));151if (c == NULL) {152hx509_clear_error_string(context);153return ENOMEM;154}155c->ops = ops;156c->ref = 1;157158ret = (*ops->init)(context, c, &c->ops_data, flags, residue, lock);159if (ret) {160free(c);161return ret;162}163164*certs = c;165return 0;166}167168/**169* Write the certificate store to stable storage.170*171* @param context A hx509 context.172* @param certs a certificate store to store.173* @param flags currently unused, use 0.174* @param lock a lock that unlocks the certificates store, use NULL to175* select no password/certifictes/prompt lock (see @ref page_lock).176*177* @return Returns an hx509 error code. HX509_UNSUPPORTED_OPERATION if178* the certificate store doesn't support the store operation.179*180* @ingroup hx509_keyset181*/182183int184hx509_certs_store(hx509_context context,185hx509_certs certs,186int flags,187hx509_lock lock)188{189if (certs->ops->store == NULL) {190hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,191"keystore if type %s doesn't support "192"store operation",193certs->ops->name);194return HX509_UNSUPPORTED_OPERATION;195}196197return (*certs->ops->store)(context, certs, certs->ops_data, flags, lock);198}199200201hx509_certs202hx509_certs_ref(hx509_certs certs)203{204if (certs == NULL)205return NULL;206if (certs->ref == 0)207_hx509_abort("certs refcount == 0 on ref");208if (certs->ref == UINT_MAX)209_hx509_abort("certs refcount == UINT_MAX on ref");210certs->ref++;211return certs;212}213214/**215* Free a certificate store.216*217* @param certs certificate store to free.218*219* @ingroup hx509_keyset220*/221222void223hx509_certs_free(hx509_certs *certs)224{225if (*certs) {226if ((*certs)->ref == 0)227_hx509_abort("cert refcount == 0 on free");228if (--(*certs)->ref > 0)229return;230231(*(*certs)->ops->free)(*certs, (*certs)->ops_data);232free(*certs);233*certs = NULL;234}235}236237/**238* Start the integration239*240* @param context a hx509 context.241* @param certs certificate store to iterate over242* @param cursor cursor that will keep track of progress, free with243* hx509_certs_end_seq().244*245* @return Returns an hx509 error code. HX509_UNSUPPORTED_OPERATION is246* returned if the certificate store doesn't support the iteration247* operation.248*249* @ingroup hx509_keyset250*/251252int253hx509_certs_start_seq(hx509_context context,254hx509_certs certs,255hx509_cursor *cursor)256{257int ret;258259if (certs->ops->iter_start == NULL) {260hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,261"Keyset type %s doesn't support iteration",262certs->ops->name);263return HX509_UNSUPPORTED_OPERATION;264}265266ret = (*certs->ops->iter_start)(context, certs, certs->ops_data, cursor);267if (ret)268return ret;269270return 0;271}272273/**274* Get next ceritificate from the certificate keystore pointed out by275* cursor.276*277* @param context a hx509 context.278* @param certs certificate store to iterate over.279* @param cursor cursor that keeps track of progress.280* @param cert return certificate next in store, NULL if the store281* contains no more certificates. Free with hx509_cert_free().282*283* @return Returns an hx509 error code.284*285* @ingroup hx509_keyset286*/287288int289hx509_certs_next_cert(hx509_context context,290hx509_certs certs,291hx509_cursor cursor,292hx509_cert *cert)293{294*cert = NULL;295return (*certs->ops->iter)(context, certs, certs->ops_data, cursor, cert);296}297298/**299* End the iteration over certificates.300*301* @param context a hx509 context.302* @param certs certificate store to iterate over.303* @param cursor cursor that will keep track of progress, freed.304*305* @return Returns an hx509 error code.306*307* @ingroup hx509_keyset308*/309310int311hx509_certs_end_seq(hx509_context context,312hx509_certs certs,313hx509_cursor cursor)314{315(*certs->ops->iter_end)(context, certs, certs->ops_data, cursor);316return 0;317}318319/**320* Iterate over all certificates in a keystore and call an function321* for each fo them.322*323* @param context a hx509 context.324* @param certs certificate store to iterate over.325* @param func function to call for each certificate. The function326* should return non-zero to abort the iteration, that value is passed327* back to the caller of hx509_certs_iter_f().328* @param ctx context variable that will passed to the function.329*330* @return Returns an hx509 error code.331*332* @ingroup hx509_keyset333*/334335int336hx509_certs_iter_f(hx509_context context,337hx509_certs certs,338int (*func)(hx509_context, void *, hx509_cert),339void *ctx)340{341hx509_cursor cursor;342hx509_cert c;343int ret;344345ret = hx509_certs_start_seq(context, certs, &cursor);346if (ret)347return ret;348349while (1) {350ret = hx509_certs_next_cert(context, certs, cursor, &c);351if (ret)352break;353if (c == NULL) {354ret = 0;355break;356}357ret = (*func)(context, ctx, c);358hx509_cert_free(c);359if (ret)360break;361}362363hx509_certs_end_seq(context, certs, cursor);364365return ret;366}367368/**369* Iterate over all certificates in a keystore and call an function370* for each fo them.371*372* @param context a hx509 context.373* @param certs certificate store to iterate over.374* @param func function to call for each certificate. The function375* should return non-zero to abort the iteration, that value is passed376* back to the caller of hx509_certs_iter().377*378* @return Returns an hx509 error code.379*380* @ingroup hx509_keyset381*/382383#ifdef __BLOCKS__384385static int386certs_iter(hx509_context context, void *ctx, hx509_cert cert)387{388int (^func)(hx509_cert) = ctx;389return func(cert);390}391392/**393* Iterate over all certificates in a keystore and call an block394* for each fo them.395*396* @param context a hx509 context.397* @param certs certificate store to iterate over.398* @param func block to call for each certificate. The function399* should return non-zero to abort the iteration, that value is passed400* back to the caller of hx509_certs_iter().401*402* @return Returns an hx509 error code.403*404* @ingroup hx509_keyset405*/406407int408hx509_certs_iter(hx509_context context,409hx509_certs certs,410int (^func)(hx509_cert))411{412return hx509_certs_iter_f(context, certs, certs_iter, func);413}414#endif415416417/**418* Function to use to hx509_certs_iter_f() as a function argument, the419* ctx variable to hx509_certs_iter_f() should be a FILE file descriptor.420*421* @param context a hx509 context.422* @param ctx used by hx509_certs_iter_f().423* @param c a certificate424*425* @return Returns an hx509 error code.426*427* @ingroup hx509_keyset428*/429430int431hx509_ci_print_names(hx509_context context, void *ctx, hx509_cert c)432{433Certificate *cert;434hx509_name n;435char *s, *i;436437cert = _hx509_get_cert(c);438439_hx509_name_from_Name(&cert->tbsCertificate.subject, &n);440hx509_name_to_string(n, &s);441hx509_name_free(&n);442_hx509_name_from_Name(&cert->tbsCertificate.issuer, &n);443hx509_name_to_string(n, &i);444hx509_name_free(&n);445fprintf(ctx, "subject: %s\nissuer: %s\n", s, i);446free(s);447free(i);448return 0;449}450451/**452* Add a certificate to the certificiate store.453*454* The receiving keyset certs will either increase reference counter455* of the cert or make a deep copy, either way, the caller needs to456* free the cert itself.457*458* @param context a hx509 context.459* @param certs certificate store to add the certificate to.460* @param cert certificate to add.461*462* @return Returns an hx509 error code.463*464* @ingroup hx509_keyset465*/466467int468hx509_certs_add(hx509_context context, hx509_certs certs, hx509_cert cert)469{470if (certs->ops->add == NULL) {471hx509_set_error_string(context, 0, ENOENT,472"Keyset type %s doesn't support add operation",473certs->ops->name);474return ENOENT;475}476477return (*certs->ops->add)(context, certs, certs->ops_data, cert);478}479480/**481* Find a certificate matching the query.482*483* @param context a hx509 context.484* @param certs certificate store to search.485* @param q query allocated with @ref hx509_query functions.486* @param r return certificate (or NULL on error), should be freed487* with hx509_cert_free().488*489* @return Returns an hx509 error code.490*491* @ingroup hx509_keyset492*/493494int495hx509_certs_find(hx509_context context,496hx509_certs certs,497const hx509_query *q,498hx509_cert *r)499{500hx509_cursor cursor;501hx509_cert c;502int ret;503504*r = NULL;505506_hx509_query_statistic(context, 0, q);507508if (certs->ops->query)509return (*certs->ops->query)(context, certs, certs->ops_data, q, r);510511ret = hx509_certs_start_seq(context, certs, &cursor);512if (ret)513return ret;514515c = NULL;516while (1) {517ret = hx509_certs_next_cert(context, certs, cursor, &c);518if (ret)519break;520if (c == NULL)521break;522if (_hx509_query_match_cert(context, q, c)) {523*r = c;524break;525}526hx509_cert_free(c);527}528529hx509_certs_end_seq(context, certs, cursor);530if (ret)531return ret;532/**533* Return HX509_CERT_NOT_FOUND if no certificate in certs matched534* the query.535*/536if (c == NULL) {537hx509_clear_error_string(context);538return HX509_CERT_NOT_FOUND;539}540541return 0;542}543544/**545* Filter certificate matching the query.546*547* @param context a hx509 context.548* @param certs certificate store to search.549* @param q query allocated with @ref hx509_query functions.550* @param result the filtered certificate store, caller must free with551* hx509_certs_free().552*553* @return Returns an hx509 error code.554*555* @ingroup hx509_keyset556*/557558int559hx509_certs_filter(hx509_context context,560hx509_certs certs,561const hx509_query *q,562hx509_certs *result)563{564hx509_cursor cursor;565hx509_cert c;566int ret, found = 0;567568_hx509_query_statistic(context, 0, q);569570ret = hx509_certs_init(context, "MEMORY:filter-certs", 0,571NULL, result);572if (ret)573return ret;574575ret = hx509_certs_start_seq(context, certs, &cursor);576if (ret) {577hx509_certs_free(result);578return ret;579}580581c = NULL;582while (1) {583ret = hx509_certs_next_cert(context, certs, cursor, &c);584if (ret)585break;586if (c == NULL)587break;588if (_hx509_query_match_cert(context, q, c)) {589hx509_certs_add(context, *result, c);590found = 1;591}592hx509_cert_free(c);593}594595hx509_certs_end_seq(context, certs, cursor);596if (ret) {597hx509_certs_free(result);598return ret;599}600601/**602* Return HX509_CERT_NOT_FOUND if no certificate in certs matched603* the query.604*/605if (!found) {606hx509_certs_free(result);607hx509_clear_error_string(context);608return HX509_CERT_NOT_FOUND;609}610611return 0;612}613614615static int616certs_merge_func(hx509_context context, void *ctx, hx509_cert c)617{618return hx509_certs_add(context, (hx509_certs)ctx, c);619}620621/**622* Merge a certificate store into another. The from store is keep623* intact.624*625* @param context a hx509 context.626* @param to the store to merge into.627* @param from the store to copy the object from.628*629* @return Returns an hx509 error code.630*631* @ingroup hx509_keyset632*/633634int635hx509_certs_merge(hx509_context context, hx509_certs to, hx509_certs from)636{637if (from == NULL)638return 0;639return hx509_certs_iter_f(context, from, certs_merge_func, to);640}641642/**643* Same a hx509_certs_merge() but use a lock and name to describe the644* from source.645*646* @param context a hx509 context.647* @param to the store to merge into.648* @param lock a lock that unlocks the certificates store, use NULL to649* select no password/certifictes/prompt lock (see @ref page_lock).650* @param name name of the source store651*652* @return Returns an hx509 error code.653*654* @ingroup hx509_keyset655*/656657int658hx509_certs_append(hx509_context context,659hx509_certs to,660hx509_lock lock,661const char *name)662{663hx509_certs s;664int ret;665666ret = hx509_certs_init(context, name, 0, lock, &s);667if (ret)668return ret;669ret = hx509_certs_merge(context, to, s);670hx509_certs_free(&s);671return ret;672}673674/**675* Get one random certificate from the certificate store.676*677* @param context a hx509 context.678* @param certs a certificate store to get the certificate from.679* @param c return certificate, should be freed with hx509_cert_free().680*681* @return Returns an hx509 error code.682*683* @ingroup hx509_keyset684*/685686int687hx509_get_one_cert(hx509_context context, hx509_certs certs, hx509_cert *c)688{689hx509_cursor cursor;690int ret;691692*c = NULL;693694ret = hx509_certs_start_seq(context, certs, &cursor);695if (ret)696return ret;697698ret = hx509_certs_next_cert(context, certs, cursor, c);699if (ret)700return ret;701702hx509_certs_end_seq(context, certs, cursor);703return 0;704}705706static int707certs_info_stdio(void *ctx, const char *str)708{709FILE *f = ctx;710fprintf(f, "%s\n", str);711return 0;712}713714/**715* Print some info about the certificate store.716*717* @param context a hx509 context.718* @param certs certificate store to print information about.719* @param func function that will get each line of the information, if720* NULL is used the data is printed on a FILE descriptor that should721* be passed in ctx, if ctx also is NULL, stdout is used.722* @param ctx parameter to func.723*724* @return Returns an hx509 error code.725*726* @ingroup hx509_keyset727*/728729int730hx509_certs_info(hx509_context context,731hx509_certs certs,732int (*func)(void *, const char *),733void *ctx)734{735if (func == NULL) {736func = certs_info_stdio;737if (ctx == NULL)738ctx = stdout;739}740if (certs->ops->printinfo == NULL) {741(*func)(ctx, "No info function for certs");742return 0;743}744return (*certs->ops->printinfo)(context, certs, certs->ops_data,745func, ctx);746}747748void749_hx509_pi_printf(int (*func)(void *, const char *), void *ctx,750const char *fmt, ...)751{752va_list ap;753char *str;754755va_start(ap, fmt);756vasprintf(&str, fmt, ap);757va_end(ap);758if (str == NULL)759return;760(*func)(ctx, str);761free(str);762}763764int765_hx509_certs_keys_get(hx509_context context,766hx509_certs certs,767hx509_private_key **keys)768{769if (certs->ops->getkeys == NULL) {770*keys = NULL;771return 0;772}773return (*certs->ops->getkeys)(context, certs, certs->ops_data, keys);774}775776int777_hx509_certs_keys_add(hx509_context context,778hx509_certs certs,779hx509_private_key key)780{781if (certs->ops->addkey == NULL) {782hx509_set_error_string(context, 0, EINVAL,783"keystore if type %s doesn't support "784"key add operation",785certs->ops->name);786return EINVAL;787}788return (*certs->ops->addkey)(context, certs, certs->ops_data, key);789}790791792void793_hx509_certs_keys_free(hx509_context context,794hx509_private_key *keys)795{796int i;797for (i = 0; keys[i]; i++)798hx509_private_key_free(&keys[i]);799free(keys);800}801802803