int sxi_hashop_end(sxi_hashop_t *hashop) { int rc = 0; if (!hashop || !hashop->conns) return -1; rc = sxi_hashop_batch_flush(hashop); while (hashop->finished != hashop->queries && !sxi_curlev_poll(sxi_conns_get_curlev(hashop->conns))) { /* syslog(LOG_INFO,"finished: %d, queries: %d", hashop->finished, * hashop->queries);*/ } if (hashop->finished > hashop->ok + hashop->enoent) { /* TODO: overwrite error msg or not? */ sxi_seterr(sxi_conns_get_client(hashop->conns), SXE_ECOMM, "%d failed HEAD requests", hashop->finished - hashop->ok - hashop->enoent); return -1; } else if (hashop->finished != hashop->queries) { sxi_seterr(sxi_conns_get_client(hashop->conns), SXE_ECOMM, "%d unfinished HEAD requests", hashop->queries - hashop->finished); return -1; } if (hashop->cb_fail) { sxi_seterr(sxi_conns_get_client(hashop->conns), SXE_ECOMM, "%d callback failures", hashop->cb_fail); return -1; } if (hashop->enoent) { sxc_client_t *sx = sxi_conns_get_client(hashop->conns); SXDEBUG("enoent: %d", hashop->enoent); return ENOENT; } if (!rc) sxc_clearerr(sxi_conns_get_client(hashop->conns)); return rc; }
int sxi_filter_add_cfg(struct filter_handle *fh, const char *volname, const void *cfg, unsigned int cfg_len) { struct filter_cfg *newcfg; if(!fh || !volname || !cfg || !cfg_len) return -1; if(filter_get_cfg(fh, volname)) return 0; newcfg = malloc(sizeof(struct filter_cfg)); if(!newcfg) { sxi_seterr(fh->sx, SXE_EMEM, "OOM"); return -1; } newcfg->volname = strdup(volname); if(!newcfg->volname) { free(newcfg); sxi_seterr(fh->sx, SXE_EMEM, "OOM"); return -1; } newcfg->cfg = malloc(cfg_len); if(!newcfg->cfg) { free(newcfg->volname); free(newcfg); sxi_seterr(fh->sx, SXE_EMEM, "OOM"); return -1; } memcpy(newcfg->cfg, cfg, cfg_len); newcfg->cfg_len = cfg_len; newcfg->next = fh->cfg; fh->cfg = newcfg; return 0; }
static CERTCertificate *load_cert_file(sxc_client_t *sx, const char *file, struct PK11_ctx *ctx) { const char *slot_name = "PEM Token #0"; CK_OBJECT_CLASS obj_class; CK_ATTRIBUTE attrs[/* max count of attributes */ 4]; unsigned attr_cnt = 0; CK_BBOOL cktrue = CK_TRUE; SECMODModule *mod; CERTCertificate *cert = NULL; if(!file || !ctx) { sxi_seterr(sx, SXE_EARG, "NULL argument"); return NULL; } memset(ctx, 0, sizeof(*ctx)); mod = SECMOD_LoadUserModule("library=libnsspem.so name=PEM", NULL, PR_FALSE); if (!mod || !mod->loaded) { if (mod) SECMOD_DestroyModule(mod); sxi_setsyserr(sx, SXE_ECFG, "Failed to load NSS PEM library"); return NULL; } sxi_crypto_check_ver(NULL); ctx->slot = PK11_FindSlotByName(slot_name); if (ctx->slot) { obj_class = CKO_CERTIFICATE; PK11_SETATTRS(attrs, attr_cnt, CKA_CLASS, &obj_class, sizeof(obj_class)); PK11_SETATTRS(attrs, attr_cnt, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL)); PK11_SETATTRS(attrs, attr_cnt, CKA_LABEL, (unsigned char *)file, strlen(file) + 1); if(CKO_CERTIFICATE == obj_class) { CK_BBOOL *pval = &cktrue; PK11_SETATTRS(attrs, attr_cnt, CKA_TRUST, pval, sizeof(*pval)); } ctx->obj = PK11_CreateGenericObject(ctx->slot, attrs, attr_cnt, PR_FALSE); if (!ctx->obj) { sxi_seterr(sx, SXE_ECFG, "Cannot load certificate from '%s': %s, %s", file, PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT), PR_ErrorToName(PR_GetError())); return NULL; } ctx->list = PK11_ListCertsInSlot(ctx->slot); if (ctx->list) { CERTCertListNode *node = CERT_LIST_HEAD(ctx->list); cert = node ? node->cert : NULL; } } else { sxi_seterr(sx, SXE_ECFG, "Failed to initialize NSS PEM token"); return NULL; } return cert; }
void sxi_setsyserr(sxc_client_t *sx, enum sxc_error_t err, const char *fmt, ...) { va_list ap; if(!sx) return; sxi_fmt_start(&sx->log.fmt); va_start(ap, fmt); sxi_vfmt_syserr(&sx->log.fmt, fmt, ap); va_end(ap); sxi_seterr(sx, err, "%s", sx->log.fmt.buf); }
int sxi_hashop_batch_add(sxi_hashop_t *hashop, const char *host, unsigned idx, const unsigned char *binhash, unsigned int blocksize) { unsigned hidx; int rc = 0; sxc_client_t *sx; if (!hashop) return -1; if (hashop->kind == HASHOP_SKIP) return 0; sx = sxi_conns_get_client(hashop->conns); if (!host || !binhash) { sxi_seterr(sx, SXE_EARG, "Null arg to hashop_batch_add"); return -1; } if ((hashop->current_host && strcmp(host, hashop->current_host)) || blocksize != hashop->current_blocksize || hashop->hashes_count >= DOWNLOAD_MAX_BLOCKS) { rc = sxi_hashop_batch_flush(hashop); } hashop->current_host = host; hashop->current_blocksize = blocksize; hidx = hashop->hashes_count * SXI_SHA1_TEXT_LEN; if (hashop->hashes_pos + SXI_SHA1_TEXT_LEN + 1 >= sizeof(hashop->hashes) || hidx + SXI_SHA1_TEXT_LEN >= sizeof(hashop->hexhashes)) { sxi_seterr(sx, SXE_EARG, "Out of bounds hashes_pos: %u (limit %lu), hidx: %u (limit %lu)", hashop->hashes_pos, (long)sizeof(hashop->hashes), hidx, (long)sizeof(hashop->hexhashes)); return -1; } sxi_bin2hex(binhash, SXI_SHA1_BIN_LEN, hashop->hashes + hashop->hashes_pos); memcpy(hashop->hexhashes + hidx, hashop->hashes + hashop->hashes_pos, SXI_SHA1_TEXT_LEN); SXDEBUG("hash @%d (%d): %.*s", idx, hashop->hashes_count, SXI_SHA1_TEXT_LEN, hashop->hashes + hashop->hashes_pos); hashop->hashes_pos += SXI_SHA1_TEXT_LEN; hashop->hashes[hashop->hashes_pos++] = ','; hashop->idxs_tmp[hashop->hashes_count] = idx; hashop->hashes_count++; SXDEBUG("returning: %d", rc); return rc; }
struct filter_handle *sxi_filter_gethandle(sxc_client_t *sx, const uint8_t *uuid) { struct filter_ctx *fctx; int i; fctx = sxi_get_fctx(sx); if(!fctx || fctx->filter_cnt <= 0) { SXDEBUG("No filters available"); sxi_seterr(sx, SXE_EFILTER, "No filters available"); return NULL; } for(i = 0; i < fctx->filter_cnt; i++) if(!memcmp(fctx->filters[i].uuid_bin, uuid, 16)) return &fctx->filters[i]; return NULL; }
int sxi_sslctxfun(sxc_client_t *sx, curlev_t *ev, const struct curl_tlssessioninfo *info) { CERTCertificate *cert; int rc = cert_from_sessioninfo(sx, info, &cert); if (rc) return rc; sxi_conns_t *conns = sxi_curlev_get_conns(ev); const char *hostname = sxi_conns_get_sslname(conns); SECStatus ret = CERT_VerifyCertName(cert, hostname); if (ret == SECSuccess) { sxi_curlev_set_verified(ev, 1); } else { PRInt32 err = PR_GetError(); sxi_seterr(sx, SXE_ECOMM, "Certificate is not valid for cluster '%s': %s", hostname, PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT)); sxi_curlev_set_verified(ev, -1); return -1; } return 0; }
static int cert_from_sessioninfo(sxc_client_t *sx, const struct curl_tlssessioninfo *info, CERTCertificate **cert) { if (info->backend != CURLSSLBACKEND_NSS) { curl_version_info_data *data = curl_version_info(CURLVERSION_NOW); sxi_seterr(sx, SXE_ECURL, "SSL backend mismatch: NSS expected, got %d (curl %s)", info->backend, data->ssl_version ? data->ssl_version : "N/A"); return -1; } PRFileDesc *desc = info->internals; if(!desc) { SXDEBUG("NULL PRFileDesc context"); return -EAGAIN; } *cert = SSL_PeerCertificate(desc); if (!*cert) { PRInt32 err = PR_GetError(); SXDEBUG("Unable to retrieve certificate for cluster: %s", PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT)); return -EAGAIN; } return 0; }
void sxi_setclusterr(sxc_client_t *sx, const char *nodeid, const char *reqid, int status, const char *msg, const char *details) { char httpcode[16]; if (!sx) return; if (!*msg) { snprintf(httpcode, sizeof(httpcode), "HTTP code %d", status); msg = httpcode; } sxi_fmt_start(&sx->log.fmt); sxi_fmt_msg(&sx->log.fmt, "Failed to %s: %s (", sx->op ? sx->op : "query cluster", msg); if (sx->op_host) { sxi_fmt_msg(&sx->log.fmt, "sx://%s", sx->op_host); if (sx->op_vol) { sxi_fmt_msg(&sx->log.fmt, "/%s", sx->op_vol); if (sx->op_path) { sxi_fmt_msg(&sx->log.fmt, "/%s", sx->op_path); } } } sxi_fmt_msg(&sx->log.fmt," on"); if (nodeid) sxi_fmt_msg(&sx->log.fmt, " node:%s", nodeid); if (reqid) sxi_fmt_msg(&sx->log.fmt, " reqid:%s", reqid); sxi_fmt_msg(&sx->log.fmt, ")"); if (status < 400 || status >= 500) { /* do not print details on 40x */ if (sx->verbose && details && *details) { sxi_fmt_msg(&sx->log.fmt, "\nHTTP %d: %s", status, details); } } sxi_seterr(sx, SXE_ECOMM, "%s", sx->log.fmt.buf); sxi_clear_operation(sx); SXDEBUG("Cluster query failed (HTTP %d): %s", status, sx->errbuf); if (details && *details) SXDEBUG("Cluster error: %s", details); }
int sxi_vcrypt_get_cert_fingerprint(sxc_client_t *sx, const char *file, uint8_t *hash, unsigned int *len) { struct PK11_ctx ctx; CERTCertificate *cert = load_cert_file(sx, file, &ctx); if(!cert) { free_PK11_ctx(&ctx); return -1; } if(sxi_sha1_calc(NULL, 0, cert->derCert.data, cert->derCert.len, hash)) { sxi_seterr(sx, SXE_ECRYPT, "Failed to compute ca fingerprint"); CERT_DestroyCertificate(cert); free_PK11_ctx(&ctx); return -1; } if(len) *len = SXI_SHA1_BIN_LEN; CERT_DestroyCertificate(cert); free_PK11_ctx(&ctx); return 0; }
static int filter_loadall(sxc_client_t *sx, const char *filter_dir) { struct filter_ctx *fctx; DIR *dir; struct dirent *dent; struct stat sb; char *path; int ret = 0, pcnt = 0; if(!sx || !filter_dir) return 1; fctx = sxi_get_fctx(sx); if(fctx->filter_cnt == -1) { SXDEBUG("Filter subsystem not available"); sxi_seterr(sx, SXE_EFILTER, "Filter subsystem not available"); return 1; } if(!(dir = opendir(filter_dir))) { SXDEBUG("Can't open filter directory %s", filter_dir); sxi_seterr(sx, SXE_EFILTER, "Can't open filter directory %s", filter_dir); return 1; } while((dent = readdir(dir))) { if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) { unsigned int pathlen = strlen(filter_dir) + strlen(dent->d_name) + 2; path = malloc(pathlen); if(!path) { SXDEBUG("OOM allocating path"); sxi_setsyserr(sx, SXE_EMEM, "OOM allocating path"); closedir(dir); return 1; } snprintf(path, pathlen, "%s/%s", filter_dir, dent->d_name); /* FIXME: path separator */ if(lstat(path, &sb) == -1) { free(path); continue; } if(S_ISDIR(sb.st_mode)) { ret = filter_loadall(sx, path); } else if(S_ISREG(sb.st_mode) && !strncmp(dent->d_name, "libsxf_", 7) && strstr(dent->d_name, ".so")) { ret = filter_register(sx, path); if(!ret) pcnt++; else if(ret == 2) ret = 0; } free(path); } } closedir(dir); if(!pcnt && ret) { sxi_seterr(sx, SXE_EFILTER, "No filters loaded due to version mismatch"); return ret; } /* if(pcnt) SXDEBUG("Loaded %d filter(s) from %s", pcnt, filter_dir); */ return 0; }
static int sxi_hashop_batch(sxi_hashop_t *hashop) { const char *host, *hexhashes; unsigned int blocksize, i, n; sxi_query_t *query; int rc; if (!hashop || !hashop->conns) return -1; host = hashop->current_host; hexhashes = hashop->hexhashes; if (!host || !hexhashes) return -1; blocksize = hashop->current_blocksize; hashop->hashes[hashop->hashes_pos] = '\0'; hashop->hexhashes[hashop->hashes_count*SXI_SHA1_TEXT_LEN] = '\0'; sxc_client_t *sx; sx = sxi_conns_get_client(hashop->conns); if (hashop->finished > hashop->ok + hashop->enoent) { sxi_seterr(sx, SXE_ECOMM, "%d failed HEAD requests", hashop->finished - hashop->ok - hashop->enoent); return -1; } struct hashop_ctx *pp = calloc(1, sizeof(*pp)); if (!pp) { sxi_seterr(sx, SXE_EMEM, "failed to allocate presence parser"); return -1; } memcpy(pp->idxs, hashop->idxs_tmp, sizeof(hashop->idxs_tmp)); curlev_context_t *cbdata = sxi_cbdata_create_hashop(hashop->conns, batch_finish, pp); if (!cbdata) { sxi_seterr(sx, SXE_EMEM, "failed to allocate callback"); free(pp); return -1; } pp->hexhashes = strdup(hexhashes); if (!pp->hexhashes) { sxi_seterr(sx, SXE_EMEM, "failed to allocate hashbatch"); free(pp); free(cbdata); return -1; } pp->hashop = hashop; SXDEBUG("a->queries: %d", hashop->queries); hashop->queries += strlen(hexhashes) / 40; SXDEBUG("hashop %d, on %s, hexhashes: %s (%p)", hashop->hashes_count, hashop->hashes, hashop->hexhashes, hashop->hexhashes); n = strlen(hexhashes) / SXI_SHA1_TEXT_LEN; switch (hashop->kind) { case HASHOP_CHECK: query = sxi_hashop_proto_check(sxi_conns_get_client(hashop->conns), blocksize, hashop->hashes, hashop->hashes_pos); break; case HASHOP_RESERVE: query = sxi_hashop_proto_reserve(sxi_conns_get_client(hashop->conns), blocksize, hashop->hashes, hashop->hashes_pos, &hashop->global_vol_id, &hashop->reserve_id, &hashop->revision_id, hashop->replica, hashop->op_expires_at); break; case HASHOP_INUSE: query = sxi_hashop_proto_inuse_begin(sxi_conns_get_client(hashop->conns), hashop->has_reserve_id ? &hashop->reserve_id : NULL); for (i=0;i < n;i++) { block_meta_entry_t entry; block_meta_t meta; if (hex2bin(hexhashes + i * SXI_SHA1_TEXT_LEN, SXI_SHA1_TEXT_LEN, meta.hash.b, sizeof(meta.hash.b))) { sxi_query_free(query); query = NULL; break; } memcpy(&entry.revision_id, &hashop->revision_id, sizeof(entry.revision_id)); memcpy(&entry.global_vol_id, &hashop->global_vol_id, sizeof(entry.global_vol_id)); entry.replica = hashop->replica; meta.entries = &entry; meta.count = 1; meta.blocksize = hashop->current_blocksize; query = sxi_hashop_proto_inuse_hash(sx, query, &meta); } query = sxi_hashop_proto_inuse_end(sx, query); break; default: query = NULL; break; } pp->query = query; if (query) { DEBUG("Sending query to %s", query->path); rc = sxi_cluster_query_ev(cbdata, hashop->conns, host, query->verb, query->path, query->content, query->content_len, presence_setup_cb, presence_cb); } else { free(pp->hexhashes); sxi_query_free(pp->query); free(pp); rc = -1; } sxi_cbdata_unref(&cbdata); return rc; }
CURLcode sxi_verifyhost(sxc_client_t *sx, const char *hostname, X509 *server_cert) { int matched = -1; /* -1 is no alternative match yet, 1 means match and 0 means mismatch */ STACK_OF(GENERAL_NAME) *altnames; CURLcode res = CURLE_OK; /* get a "list" of alternative names */ altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL); if(altnames) { int numalts; int i; /* get amount of alternatives, RFC2459 claims there MUST be at least one, but we don't depend on it... */ numalts = sk_GENERAL_NAME_num(altnames); /* loop through all alternatives while none has matched */ for(i=0; (i<numalts) && (matched != 1); i++) { /* get a handle to alternative name number i */ const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i); /* only check alternatives of the same type the target is */ if(check->type == GEN_DNS) { /* get data and length */ const char *altptr = (char *)ASN1_STRING_data(check->d.ia5); size_t altlen = (size_t) ASN1_STRING_length(check->d.ia5); /* name/pattern comparison */ /* The OpenSSL man page explicitly says: "In general it cannot be assumed that the data returned by ASN1_STRING_data() is null terminated or does not contain embedded nulls." But also that "The actual format of the data will depend on the actual string type itself: for example for and IA5String the data will be ASCII" Curl uses strlen(altptr) == altlen here to check for embedded \0s. We use memchr() to be safer from a possible non-null terminated string. */ if(!memchr(altptr, 0, altlen) && /* if this isn't true, there was an embedded zero in the name string and we cannot match it. */ Curl_cert_hostcheck(altptr, hostname)) matched = 1; else matched = 0; } } GENERAL_NAMES_free(altnames); } if(matched == 1) /* an alternative name matched the server hostname */ SXDEBUG("\t subjectAltName: %s matched\n", hostname); else if(matched == 0) { /* an alternative name field existed, but didn't match and then we MUST fail */ sxi_seterr(sx, SXE_ECOMM, "subjectAltName does not match %s\n", hostname); res = CURLE_PEER_FAILED_VERIFICATION; } else { /* we have to look to the last occurrence of a commonName in the distinguished one to get the most significant one. */ int j,i=-1 ; /* The following is done because of a bug in 0.9.6b */ unsigned char *nulstr = (unsigned char *)""; unsigned char *peer_CN = nulstr; X509_NAME *name = X509_get_subject_name(server_cert) ; if(name) while((j = X509_NAME_get_index_by_NID(name, NID_commonName, i))>=0) i=j; /* we have the name entry and we will now convert this to a string that we can use for comparison. Doing this we support BMPstring, UTF8 etc. */ if(i>=0) { ASN1_STRING *tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name,i)); /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input is already UTF-8 encoded. We check for this case and copy the raw string manually to avoid the problem. This code can be made conditional in the future when OpenSSL has been fixed. Work-around brought by Alexis S. L. Carvalho. */ if(tmp) { if(ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) { j = ASN1_STRING_length(tmp); if(j >= 0) { peer_CN = OPENSSL_malloc(j+1); if(peer_CN) { memcpy(peer_CN, ASN1_STRING_data(tmp), j); peer_CN[j] = '\0'; } } } else /* not a UTF8 name */ j = ASN1_STRING_to_UTF8(&peer_CN, tmp); if(peer_CN && memchr(peer_CN, 0, j)) { /* there was a terminating zero before the end of string, this cannot match and we return failure! */ sxi_seterr(sx, SXE_ECOMM, "SSL: illegal cert name field"); res = CURLE_PEER_FAILED_VERIFICATION; } } } if(peer_CN == nulstr) peer_CN = NULL; /* curl would convert from UTF8 to host encoding if built with HAVE_ICONV (not default) */ if(res) /* error already detected, pass through */ ; else if(!peer_CN) { SXDEBUG("SSL: unable to obtain common name from peer certificate"); res = CURLE_PEER_FAILED_VERIFICATION; } else if(!Curl_cert_hostcheck((const char *)peer_CN, hostname)) { sxi_seterr(sx, SXE_ECOMM, "SSL: certificate subject name '%s' does not match " "target host name '%s'", peer_CN, hostname); res = CURLE_PEER_FAILED_VERIFICATION; } else { SXDEBUG("\t common name: %s (matched)\n", peer_CN); } if(peer_CN) OPENSSL_free(peer_CN); } return res; }