/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Netscape security libraries. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1994-2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. */ #include "certlist.h" #include "nlsutil.h" #include "base64.h" #include "cert.h" #include "certdb.h" #include "textgen.h" #include "minihttp.h" #include "advisor.h" #include "slist.h" #include "nspr.h" #include "nlslayer.h" enum { CERTLIST_PARAM_KEY = (PRIntn) 0, CERTLIST_PARAM_USAGE, CERTLIST_PARAM_PREFIX, /* CERTLIST_PARAM_WRAPPER, -jp not used, as we format all certs together */ CERTLIST_PARAM_SUFFIX, CERTLIST_PARAM_COUNT }; SSMSortedListFn list_functions; #define WRAPPER "cert_list_item" #define EMAIL_WRAPPER "cert_list_email_item" typedef enum { clNick, clCertID, /* issuer and serial number */ clRID, clEmail } ssmCLCertKey; typedef struct { SSMTextGenContext *cx; char *output; char *temp; char *fmt; char *efmt; void *datefmt; CERTCertDBHandle * db; SSMSortedList **hash; /* Hash to contain list of certs to display */ ssmCLCertKey key; ssmCLCertUsage usage; } ssmCLState; static SSMStatus format_raw_cert(CERTCertificate *cert, char *wrapper, void*dfmt, char **result); /* Hash table functions */ PRIntn ssm_certhash_rid_compare(const void *v1, const void *v2); PLHashNumber ssm_certhash_issuersn_hash(const void *v); PRIntn ssm_certhash_issuersn_compare(const void *v1, const void *v2); PRIntn ssm_pointer_compare(const void *v1, const void *v2); PRIntn ssm_certhash_cert_compare(const void *v1, const void *v2); void * ssm_certhash_alloc_table(void *state, PRSize size); void ssm_certhash_free_table(void *state, void *item); PLHashEntry * ssm_certhash_alloc_entry(void *state, const void *key); void ssm_certhash_free_entry(void *s, PLHashEntry *he, PRUintn flag); void certlist_free_item(SECItem *item); void certlist_dereturnify(char *s); SSMStatus ssm_certlist_get_b64_cert_key(CERTCertificate *cert, char **result); void * certlist_get_cert_key(ssmCLState *state, CERTCertificate *cert, ssmCLCertUsage usage); PRBool certlist_include_cert(CERTCertificate * cert, ssmCLState *state, ssmCLCertUsage * usage); SECStatus certlist_get_cert(CERTCertificate *cert, SECItem *dbkey, void *arg); SSMStatus certlist_display_cert(PRIntn index, void * arg, void * key, void * itemdata); PRIntn certlist_wrap_cert(PLHashEntry *he, PRIntn index, void *arg); SSMStatus certlist_get_usage(ssmCLState *state); SSMStatus certlist_get_key_type(ssmCLState *state); SSMStatus ssm_populate_key_hash(ssmCLState * state); PRIntn certlist_rid_compare(const void *v1, const void *v2) { SSMResourceID rid1 = (SSMResourceID) v1; SSMResourceID rid2 = (SSMResourceID) v2; PRIntn rv; if (rid1 < rid2) rv = -1; else if (rid1 > rid2) rv = 1; else rv = 0; return rv; } PRIntn certlist_compare_strings(const void * v1, const void *v2) { return PL_strcasecmp((char *)v1, (char *)v2); } PRIntn certlist_issuersn_compare(const void *v1, const void *v2) { PRIntn rv; SECComparison cmp = SECLessThan; /* this is arbitrary */ const CERTIssuerAndSN *i1 = (const CERTIssuerAndSN *) v1; const CERTIssuerAndSN *i2 = (const CERTIssuerAndSN *) v2; PR_ASSERT(i1); PR_ASSERT(i2); if (!i1 || !i2) goto loser; cmp = SECITEM_CompareItem(&(i1->derIssuer), &(i2->derIssuer)); if (cmp == SECEqual) cmp = SECITEM_CompareItem(&(i2->serialNumber), &(i2->serialNumber)); loser: /* Don't need to free i1 and i2, since they are allocated out of the certs' arenas. They will be freed when the certs are freed. */ rv = (PRIntn) cmp; return rv; } /* Free ops for cert list */ void certlist_free_data(void * data) { /* Free the cert, then (if asked) free the entry. */ PR_Free(((ssmCertData *)data)->certEntry); PR_Free((ssmCertData *)data); } void certlist_none(void * data) { return; } void certlist_free_string(void * key) { PR_Free((char *)key); } /* Utility routines for cert list keyword handler below */ void certlist_free_item(SECItem *item) { if (item) { if (item->data) PR_Free(item->data); PR_Free(item); } } void certlist_dereturnify(char *s) { char *c = s; while(*c) { if ((*c == '\015') || (*c == '\012')) { char *c2 = c; while ((*c2 == '\015') || (*c2 == '\012')) c2++; /* move the rest of the string on top of c */ memmove(c, c2, strlen(c2)); } c++; } } SSMStatus ssm_certlist_get_b64_cert_key(CERTCertificate *cert, char **result) { SECItem *certKeyItem; CERTIssuerAndSN *isn = NULL; SSMStatus rv = SSM_SUCCESS; PR_ASSERT(cert && result); *result = NULL; /* in case we fail */ isn = CERT_GetCertIssuerAndSN(NULL, cert); if (!isn) goto loser; certKeyItem = (SECItem *) PR_CALLOC(sizeof(SECItem)); if (!certKeyItem) goto loser; certKeyItem->len = isn->serialNumber.len + isn->derIssuer.len; certKeyItem->data = (unsigned char*)PR_CALLOC(certKeyItem->len); if (certKeyItem->data == NULL) goto loser; /* copy the serialNumber */ memcpy(certKeyItem->data, isn->serialNumber.data, isn->serialNumber.len); /* copy the issuer */ memcpy( &(certKeyItem->data[isn->serialNumber.len]), isn->derIssuer.data, isn->derIssuer.len); /* b64 the item */ *result = BTOA_ConvertItemToAscii(certKeyItem); certlist_dereturnify(*result); goto done; loser: rv = SSM_FAILURE; done: certlist_free_item(certKeyItem); return rv; } /* get the appropriate key depending on what key type we have chosen. */ void * certlist_get_cert_key(ssmCLState *state, CERTCertificate *cert, ssmCLCertUsage usage) { char *result_ch = NULL; void *result = NULL; SSMStatus rv = SSM_SUCCESS; PR_ASSERT(cert); if (!cert) return result; switch(state->key) { case clCertID: rv = ssm_certlist_get_b64_cert_key(cert, &result_ch); PR_ASSERT(rv == SSM_SUCCESS); result = result_ch; break; case clRID: PR_ASSERT(!"Not yet implemented."); break; case clNick: /* duplicate the nickname */ if (usage == clEmailRecipient) { PR_ASSERT(cert && cert->emailAddr); result = PR_CALLOC(strlen(cert->emailAddr)+1); if (result) PL_strcpy((char*)result, cert->emailAddr); } else { PR_ASSERT(cert && cert->nickname); result = PR_CALLOC(strlen(cert->nickname)+1); if (result) PL_strcpy((char *) result, cert->nickname); } break; case clEmail: PR_ASSERT(cert && cert->emailAddr); result = PR_CALLOC(strlen(cert->emailAddr)+1); if (result) PL_strcpy((char*)result, cert->emailAddr); break; default: PR_ASSERT(0); /* scream if we defaulted here */ } return result; } PRBool certlist_include_cert(CERTCertificate * cert, ssmCLState *state, ssmCLCertUsage * usage) { PRBool usercert = PR_FALSE; char *nickname; char *emailAddr; CERTCertTrust *trust; ssmCLCertUsage thisUsage = clNoUsage; /* jsw said: look at the base cert info stuff, not the dbEntry since * we may have been called from a PKCS#11 module */ nickname = cert->nickname; trust = cert->trust; emailAddr = cert->emailAddr; /* Don't display certs that are invisible */ if ( ( ( trust->sslFlags & CERTDB_INVISIBLE_CA ) || (trust->emailFlags & CERTDB_INVISIBLE_CA ) || (trust->objectSigningFlags & CERTDB_INVISIBLE_CA ) ) ) goto dontInclude; if (nickname) { /* it's a cert I have named. Could be a personal cert of mine, an SSL server cert, or a CA cert. Find out what kind. */ if (state->usage == clSSLClient) { if (trust->sslFlags & CERTDB_USER) { /* It's an SSL cert I own. */ thisUsage = clSSLClient; goto include; } else { goto dontInclude; } } if (state->usage == clEmailSigner) { if (trust->emailFlags & CERTDB_USER) { /* It's an email cert I own. */ thisUsage = clEmailSigner; goto include; } else { goto dontInclude; } } if (state->usage == clAllMine) { if ((trust->sslFlags & CERTDB_USER) || (trust->emailFlags & CERTDB_USER) || (trust->objectSigningFlags & CERTDB_USER)) { /* It's a cert I own. */ usercert = PR_TRUE; thisUsage = clAllMine; goto include; } else { goto dontInclude; } } /* it is an SSL site */ if (trust->sslFlags & CERTDB_VALID_PEER) { thisUsage = clSSLServer; goto include; } /* CA certs */ if ((trust->sslFlags & CERTDB_VALID_CA) || (trust->emailFlags & CERTDB_VALID_CA) || (trust->objectSigningFlags & CERTDB_VALID_CA)) { thisUsage = clAllCA; goto include; } } /* end of if nickname */ /* Check if this is an email cert that belongs to someone else. */ if (emailAddr && !usercert && (trust->emailFlags & CERTDB_VALID_PEER)) { /* Someone else's email cert */ thisUsage = clEmailRecipient; goto include; } dontInclude: *usage = thisUsage; return PR_FALSE; include: *usage = thisUsage; if (state->usage == thisUsage || (state->usage == clAllMine && (thisUsage == clSSLClient || thisUsage == clEmailSigner))) { return PR_TRUE; } return PR_FALSE; } /* build/append a cert list string depending on key and usage */ SECStatus certlist_get_cert(CERTCertificate *cert, SECItem *dbkey, void *arg) { ssmCLState *state = (ssmCLState *) arg; SECStatus rv = SECSuccess; void *certHashKey; /* the key we use to insert into the hash */ ssmCLCertUsage thisUsage; ssmCertData * data; char * certInfo = NULL, *nick = NULL; char * wrapper; /* check if we want to include cert in our hash table */ if (!certlist_include_cert(cert, state, &thisUsage)) goto done; /* check if cert is already in the db */ nick = (cert->nickname)?cert->nickname:cert->emailAddr; if (SSMSortedList_Lookup(*state->hash, nick)) goto done; /* different wrapper for email certs */ if (thisUsage == clEmailRecipient) wrapper = state->efmt; else wrapper = state->fmt; /* If we get here, then we should include this cert in the hash table. */ rv = (SECStatus) (format_raw_cert(cert, wrapper, state->datefmt, &certInfo) == SSM_SUCCESS) ? SECSuccess : SECFailure; if (rv != SECSuccess) goto done; certHashKey = certlist_get_cert_key(state, cert, thisUsage); data = (ssmCertData *) PORT_ZAlloc(sizeof(ssmCertData)); data->usage = thisUsage; data->certEntry = certInfo; /* XXX the following code is necessary because we want to pick out * email signing certs among "my certs" */ if (cert->trust->emailFlags & CERTDB_USER) { data->isEmailSigner = PR_TRUE; } else { data->isEmailSigner = PR_FALSE; } SSMSortedList_Insert(*state->hash, certHashKey, data); done: return rv; } static char * escape_quotes(const char * str) { char * outstr = NULL, * ptr = NULL; int i=0, length = 0; int quotechar = 0; if (!str) goto done; ptr = str; while (*ptr) { length++; if (*ptr == 39 || *ptr == 34) quotechar++; ptr++; } outstr = (char *) PORT_ZAlloc(length+quotechar+1); if (!quotechar) { memcpy(outstr, str, length); goto done; } ptr = str; i = 0; while (i < length+quotechar) { if (*ptr == 39 || *ptr == 34) outstr[i++] = 92; outstr[i++] = *ptr; ptr++; } done: return outstr; } /* Format a raw cert into (wrapper). */ static SSMStatus format_raw_cert(CERTCertificate *cert, char *wrapper, void *dfmt, char **result) { char *nickStr = NULL; char *emailStr = NULL; char *subjectName = NULL; char *validStartStr = NULL; char *validEndStr = NULL; char * issuerName = NULL; void *dateFormat = dfmt; char *isn_ch = NULL; SSMStatus rv = SSM_SUCCESS; PR_ASSERT(wrapper); /* If a date format wasn't supplied, make one. */ if (dateFormat) { dateFormat = nlsNewDateFormat(); if (!dateFormat) { goto loser; } } /* Get the key from the cert in b64 format */ rv = ssm_certlist_get_b64_cert_key(cert, &isn_ch); if (rv != SSM_SUCCESS) goto loser; PR_ASSERT(isn_ch); nickStr = (cert->nickname) ?escape_quotes(cert->nickname):PL_strdup("-"); emailStr = (cert->emailAddr) ?escape_quotes(cert->emailAddr):PL_strdup("-"); subjectName = (cert->subjectName) ? escape_quotes(cert->subjectName) : PL_strdup("-"); issuerName = CERT_GetOrgName(&cert->issuer); issuerName = issuerName ? escape_quotes(issuerName) : PL_strdup("-"); validStartStr = DER_UTCDayToAscii(&cert->validity.notBefore); if (!validStartStr) validStartStr = PL_strdup(""); validEndStr = DER_UTCDayToAscii(&cert->validity.notAfter); if (!validEndStr) validEndStr = PL_strdup(""); SSMTextGen_UTF8StringClear(result); *result = PR_smprintf(wrapper, isn_ch, nickStr, emailStr, subjectName, validStartStr, validEndStr, issuerName, 0); SSM_DebugUTF8String("wrapped cert", *result); goto done; loser: SSM_DEBUG("Couldn't format cert!"); if (rv == SSM_SUCCESS) rv = SSM_FAILURE; done: PR_FREEIF(nickStr); PR_FREEIF(emailStr); PR_FREEIF(subjectName); PR_FREEIF(issuerName); PR_FREEIF(isn_ch); PR_FREEIF(validStartStr); PR_FREEIF(validEndStr); if (dateFormat && !dfmt) /* if we made the date format, delete it */ nlsFreeDateFormat(dateFormat); return rv; } /* Enumerator for cert hash entries. Embed cert information in the wrapper (state->wrapper). */ SSMStatus certlist_display_cert(PRIntn index, void * arg, void * key, void * itemdata) { ssmCLState *state = (ssmCLState *) arg; SSMStatus rv = SSM_SUCCESS; ssmCertData * data = (ssmCertData *) itemdata; if (state->usage == data->usage || (state->usage == clAllMine && (data->usage == clSSLClient || data->usage == clEmailSigner)) || (state->usage == clEmailSigner && data->usage == clAllMine && data->isEmailSigner)) { rv = SSM_ConcatenateUTF8String(&state->output, data->certEntry); if (rv != SSM_SUCCESS) goto loser; } goto done; loser: SSM_DEBUG("certlist_display_cert: couldn't add cert info to display list!"); done: /* keep going */ return SSM_SUCCESS; } /* get the certType parameter */ SSMStatus certlist_get_usage(ssmCLState *state) { char *usageStr = (char *) SSM_At(state->cx->m_params, CERTLIST_PARAM_USAGE); SSMStatus rv = SSM_SUCCESS; PR_ASSERT(usageStr); if (!usageStr) { SSM_HTTPReportSpecificError(state->cx->m_request, "certlist_get_usage: " "usage parameter is NULL"); goto loser; } /* Figure out which one it is. */ if (!MIN_STRCMP(usageStr, "AllMine")) state->usage = clAllMine; else if (!MIN_STRCMP(usageStr, "SSLC")) state->usage = clSSLClient; else if (!MIN_STRCMP(usageStr, "EmailS")) state->usage = clEmailSigner; else if (!MIN_STRCMP(usageStr, "SSLS")) state->usage = clSSLServer; else if (!MIN_STRCMP(usageStr, "EmailR")) state->usage = clEmailRecipient; else if (!MIN_STRCMP(usageStr, "CA")) state->usage = clAllCA; else /* bad value */ { SSM_HTTPReportSpecificError(state->cx->m_request, "_certList: Bad usage -- must be " "AllMine|SSLClient|EmailSigner|" "SSLServer|EmailRecipient|CA"); goto loser; } goto done; loser: if (rv == SSM_SUCCESS) rv = SSM_FAILURE; done: return rv; } /* get the certType parameter */ SSMStatus certlist_get_key_type(ssmCLState *state) { char *keyStr = (char *) SSM_At(state->cx->m_params, CERTLIST_PARAM_KEY); SSMStatus rv = SSM_SUCCESS; PR_ASSERT(keyStr); if (!keyStr) { SSM_HTTPReportSpecificError(state->cx->m_request, "certlist_get_key_type: " "key parameter is NULL"); goto loser; } /* Figure out which one it is. */ if (!MIN_STRCMP(keyStr, "nick")) state->key = clNick; else if (!MIN_STRCMP(keyStr, "certID")) state->key = clCertID; else if ((!MIN_STRCMP(keyStr, "rid")) ||(!MIN_STRCMP(keyStr, "RID"))) state->key = clRID; else if (!MIN_STRCMP(keyStr, "email")) state->key = clEmail; else/* bad value */ { SSM_HTTPReportSpecificError(state->cx->m_request, "_certList: Bad cert key -- must be " "nick|certID|RID"); goto loser; } goto done; loser: if (rv == SSM_SUCCESS) rv = SSM_FAILURE; done: return rv; } SSMStatus ssm_populate_key_hash(ssmCLState * state) { SSMCompare_fn keyComparer = NULL; SSMListFree_fn keyFree = NULL; SSMListFree_fn dataFree = certlist_free_data; SECStatus srv; SSMStatus rv = SSM_SUCCESS; char *tempUStr = NULL; PermCertCallback callback = certlist_get_cert; /* Based on the usage, choose hasher/key comparer functions for the hash table we create below. */ switch(state->key) { case clRID: keyComparer = certlist_rid_compare; keyFree = certlist_none; break; case clEmail: case clCertID: case clNick: keyComparer = certlist_compare_strings; keyFree = certlist_free_string; break; default: PR_ASSERT(0); } list_functions.keyCompare = keyComparer; list_functions.freeListItemData = dataFree; list_functions.freeListItemKey = keyFree; /* Create the hash table if needed */ if (!*state->hash) *state->hash = SSMSortedList_New(&list_functions); if (!*state->hash) goto loser; PR_ASSERT(state->db); /* Get the wrapper text. */ rv = SSM_GetAndExpandTextKeyedByString(state->cx, WRAPPER, &tempUStr); if (rv != SSM_SUCCESS) goto loser; /* error string set by the called function */ SSM_DebugUTF8String("Certlist wrapper", tempUStr); state->fmt = tempUStr; tempUStr = NULL; /* wrapper for email certs */ rv = SSM_GetAndExpandTextKeyedByString(state->cx, EMAIL_WRAPPER, &tempUStr); if (rv != SSM_SUCCESS) goto loser; /* error string set by the called function */ SSM_DebugUTF8String("Certlist email wrapper", tempUStr); state->efmt = tempUStr; tempUStr = NULL; /* Create a DateFormat object which we will use to convert cert date/times into displayable text. */ state->datefmt = nlsNewDateFormat(); if (!state->datefmt) { goto loser; } /* Format each cert into the list. */ srv = SEC_TraversePermCerts(state->db, callback, state); if (srv == SECSuccess) srv = PK11_TraverseSlotCerts(callback, state, state->cx->m_request->ctrlconn); goto done; loser: if (rv == SSM_SUCCESS) rv = SSM_FAILURE; done: return rv; } /* Cert list keyword handler. Syntax: {_certList ,,prefix,wrapper,suffix} where ::= "nick" | "certID" | "RID" | "email" ::= "AllMine"|"SSLClient"|"EmailSigner"| "SSLServer"|"EmailRecipient"|"CA" (for now) Generates a list of certificates. The following steps are performed: - Send {prefix} to the client. - For each cert indicated by and , format the cert data into {wrapper} and send to the client. For the moment, raw certs are used and passed to NLS_MessageFormat in the following form: NLS_FORMAT_TYPE_STRING, (cert issuer+sn base64), NLS_FORMAT_TYPE_STRING, (cert nick), NLS_FORMAT_TYPE_STRING, (cert email address), NLS_FORMAT_TYPE_STRING, (cert subject name), NLS_FORMAT_TYPE_LONG, (cert resource ID, 0 for now) - After the certs are processed and sent, send {suffix}. */ SSMStatus SSM_CertListKeywordHandler(SSMTextGenContext *cx) { SSMStatus rv = SSM_SUCCESS; char *prefix = NULL, *suffix = NULL; char *tempUStr = NULL; SSMResource * target = NULL; ssmCLState state; SSMResourceType targetType = SSM_RESTYPE_NULL; PRIntn certType[clAllCA+1]; certType[clAllMine] = 0x00000001; certType[clEmailRecipient] = 0x00000010; certType[clSSLServer] = 0x00000100; certType[clAllCA] = 0x00001000; certType[clEmailSigner] = 0x00010000; /* Check for parameter validity */ PR_ASSERT(cx); PR_ASSERT(cx->m_request); PR_ASSERT(cx->m_params); PR_ASSERT(cx->m_result); if (!cx || !cx->m_request || !cx->m_params || !cx->m_result) { rv = (SSMStatus) PR_INVALID_ARGUMENT_ERROR; goto real_loser; /* really bail here */ } state.cx = cx; state.fmt = NULL; state.efmt = NULL; state.datefmt = NULL; state.output = NULL; state.temp = NULL; state.db = cx->m_request->ctrlconn->m_certdb; if (!state.db) { SSM_HTTPReportSpecificError(state.cx->m_request, "_certlist: no cert db available"); goto user_loser; } if (SSM_Count(cx->m_params) != CERTLIST_PARAM_COUNT) { SSM_HTTPReportSpecificError(cx->m_request, "_certList: " "Incorrect number of parameters " "(%d supplied, %d needed).\n", SSM_Count(cx->m_params), CERTLIST_PARAM_COUNT); goto user_loser; } /* Convert parameters to something we can use in finding certs. */ rv = certlist_get_key_type(&state); if (rv != SSM_SUCCESS) goto user_loser; rv = certlist_get_usage(&state); if (rv != SSM_SUCCESS) goto user_loser; prefix = (char *) SSM_At(cx->m_params, CERTLIST_PARAM_PREFIX); suffix = (char *) SSM_At(cx->m_params, CERTLIST_PARAM_SUFFIX); PR_ASSERT(prefix); /* already did user check, so bomb here if null */ PR_ASSERT(suffix); /* check where we're called from */ target = SSMTextGen_GetTargetObject(cx); if (!target) goto real_loser; /* SecurityAdvisor context keeps hash around, otherwise create new one */ if (SSM_IsAKindOf(target, SSM_RESTYPE_SECADVISOR_CONTEXT)) { targetType = SSM_RESTYPE_SECADVISOR_CONTEXT; state.hash = &((SSMSecurityAdvisorContext *)target)->m_certhash; } else *state.hash = NULL; state.output = state.temp = NULL; /* Start with the prefix. */ rv = SSM_GetAndExpandTextKeyedByString(cx, prefix, &tempUStr); if (rv != SSM_SUCCESS) goto real_loser; /* error string set by the called function */ SSM_DebugUTF8String("Certlist prefix", tempUStr); rv = SSM_ConcatenateUTF8String(&cx->m_result, tempUStr); PR_Free(tempUStr); tempUStr = NULL; if (rv != SSM_SUCCESS) { goto real_loser; } /* initialize a hash if haven't already */ if ((*state.hash == NULL) || (SSM_IsAKindOf(target, SSM_RESTYPE_SECADVISOR_CONTEXT)) && ((((SSMSecurityAdvisorContext *)target)->m_certsIncluded & certType[state.usage]) == 0)) { ((SSMSecurityAdvisorContext *)target)->m_certsIncluded |= certType[state.usage]; rv = ssm_populate_key_hash(&state); if (rv != SSM_SUCCESS) goto user_loser; } /* Already have all certs info, output in a list in state.result */ SSMSortedList_Enumerate(*state.hash, certlist_display_cert, &state); if (state.output) { rv = SSM_ConcatenateUTF8String(&cx->m_result, state.output); /* Finally, add the suffix. */ rv = SSM_GetAndExpandTextKeyedByString(cx, suffix, &tempUStr); if (rv != SSM_SUCCESS) goto real_loser; /* error string set by the called function */ SSM_DebugUTF8String("certlist suffix", tempUStr); rv = SSM_ConcatenateUTF8String(&cx->m_result, tempUStr); if (rv != SSM_SUCCESS) { goto real_loser; } } else /* no certs found */ { char * tmpStr = NULL; /* release prefix */ PR_FREEIF(state.output); state.output = NULL; rv = SSM_GetAndExpandTextKeyedByString(cx, "list_empty", &tmpStr); if (rv != SSM_SUCCESS) SSM_DEBUG("SSM_CertListKeywordHandler: could not get filler for empty cert list!\n"); PR_FREEIF(cx->m_result); cx->m_result = NULL; rv = SSM_ConcatenateUTF8String(&cx->m_result, tmpStr); PR_FREEIF(tmpStr); tmpStr = NULL; } goto done; user_loser: /* If we reach this point, something in the input is wrong, but we can still send something back to the client to indicate that a problem has occurred. */ /* If we can't do what we're about to do, really bail. */ if (!cx->m_request || !cx->m_request->errormsg) goto real_loser; /* Clear the string we were accumulating. */ SSMTextGen_UTF8StringClear(&cx->m_result); rv = SSM_ConcatenateUTF8String(&cx->m_result, cx->m_request->errormsg); /* Clear the result string, since we're sending this inline */ SSMTextGen_UTF8StringClear(&cx->m_request->errormsg); goto done; real_loser: /* If we reach this point, then we are so screwed that we cannot send anything vaguely normal back to the client. Bail. */ if (rv == SSM_SUCCESS) rv = SSM_FAILURE; done: if (targetType && targetType != SSM_RESTYPE_SECADVISOR_CONTEXT) if (*state.hash) SSMSortedList_Destroy(*state.hash); PR_FREEIF(state.fmt); PR_FREEIF(state.efmt); PR_FREEIF(state.output); PR_FREEIF(state.temp); if (state.datefmt) nlsFreeDateFormat(state.datefmt); PR_FREEIF(tempUStr); return rv; } static int ssm_CertsOnSmartcards(SSMControlConnection* ctrl, char* name) { CERTCertList* list = NULL; int numCerts = 0; list = PK11_FindCertsFromNickname(name, (void*)ctrl); if (list == NULL) { return 0; } numCerts = SSM_CertListCount(list); CERT_DestroyCertList(list); return numCerts; } static PRBool ssm_OtherCertsByNicknameOrEmailAddr(SSMControlConnection* ctrl, char* name) { int numCerts = 0; /* first get the number of certs under the nickname */ numCerts = CERT_NumPermCertsForNickname(ctrl->m_certdb, name); if (numCerts == 0) { /* we may not see the cert because it's on external token: check it */ numCerts = ssm_CertsOnSmartcards(ctrl, name); } if (numCerts > 1) { /* note that the cert in question is still in the DB: it is just * marked for deletion, but not deleted yet */ return PR_TRUE; } else if (numCerts == 1) { /* it is the only cert */ return PR_FALSE; } else { /* nickname may be email address: try the email address */ CERTCertList* list = NULL; int count = 0; list = CERT_CreateEmailAddrCertList(list, ctrl->m_certdb, name, PR_Now(), PR_FALSE); if (list == NULL) { /* this usually shouldn't happen */ return PR_FALSE; } count = SSM_CertListCount(list); if (list != NULL) { CERT_DestroyCertList(list); } if (count > 1) { return PR_TRUE; } else { return PR_FALSE; } } } SSMStatus SSM_ChangeCertSecAdvisorList(HTTPRequest * req, char * nickname, ssmCertHashAction action) { SSMStatus rv = SSM_SUCCESS; SSMControlConnection * ctrl = req->ctrlconn; CERTCertificate * dbCert = NULL; PRIntn i; SSMSecurityAdvisorContext * advisor = NULL; SSMResourceID resID; char * tmp; SSMSortedList * hash; PR_ASSERT(ctrl && SSM_IsAKindOf((SSMResource *)ctrl, SSM_RESTYPE_CONTROL_CONNECTION)); if (!ctrl->m_secAdvisorList || !ctrl->m_secAdvisorList->len) goto done; if (action == certHashRemove) /* check if need to delete */ { PR_ASSERT(nickname); /* find out if there are other certs under the same nickname than * the one marked for deletion */ if (ssm_OtherCertsByNicknameOrEmailAddr(ctrl, nickname)) { goto done; } } /* there might be multiple Security Advisor up */ for (i=0; im_secAdvisorList->len; i++){ resID = ctrl->m_secAdvisorList->data[i]; rv = (SSMStatus) SSMControlConnection_GetResource(ctrl, resID, (SSMResource **)&advisor); if (rv != SSM_SUCCESS) continue; PR_ASSERT(advisor && SSM_IsAKindOf((SSMResource *)advisor, SSM_RESTYPE_SECADVISOR_CONTEXT)); if (!advisor->m_certhash) continue; hash = advisor->m_certhash; if (action == certHashRemove) SSMSortedList_Remove(hash, nickname, (void **)&tmp); else if (action == certHashAdd) { ssmCLState state; char * form; SSMTextGenContext *cx; state.key = clNick; state.hash = &advisor->m_certhash; state.db = ctrl->m_certdb; /* this assumes we're called from Restore certificate or * or some other place that includes form name in HTTP request */ rv = SSM_HTTPParamValue(req, "formName", &form); if (strstr(form, "mine")) state.usage = clAllMine; else if (strstr(form, "others")) state.usage = clEmailRecipient; else if (strstr(form, "websites")) state.usage = clSSLServer; else if (strstr(form, "authorities")) state.usage = clAllCA; rv = SSMTextGen_NewTopLevelContext(req, &cx); if (rv != SSM_SUCCESS) { SSM_DEBUG("ChangeCertSecAdvisorList: can't get TextGenContext\n"); goto done; } state.cx = cx; rv = ssm_populate_key_hash(&state); if (rv != SSM_SUCCESS) SSM_DEBUG("ChangeCertSecAdvisorList: error reading certs\n"); SSMTextGen_DestroyContext(cx); } else SSM_DEBUG("ChangeCertSecAdvisorList: bad change request %d\n", action); SSM_FreeResource((SSMResource *)advisor); advisor = NULL; } done: if (dbCert) CERT_DestroyCertificate(dbCert); return rv; }