int fko_verify_hmac(fko_ctx_t ctx, const char * const hmac_key, const int hmac_key_len) { char *hmac_digest_from_data = NULL; char *tbuf = NULL; int res = FKO_SUCCESS; int hmac_b64_digest_len = 0, zero_free_rv = FKO_SUCCESS; /* Must be initialized */ if(!CTX_INITIALIZED(ctx)) return(FKO_ERROR_CTX_NOT_INITIALIZED); if (! is_valid_encoded_msg_len(ctx->encrypted_msg_len)) return(FKO_ERROR_INVALID_DATA_HMAC_MSGLEN_VALIDFAIL); if(hmac_key_len > MAX_DIGEST_BLOCK_LEN) return(FKO_ERROR_INVALID_HMAC_KEY_LEN); if(ctx->hmac_type == FKO_HMAC_MD5) hmac_b64_digest_len = MD5_B64_LEN; else if(ctx->hmac_type == FKO_HMAC_SHA1) hmac_b64_digest_len = SHA1_B64_LEN; else if(ctx->hmac_type == FKO_HMAC_SHA256) hmac_b64_digest_len = SHA256_B64_LEN; else if(ctx->hmac_type == FKO_HMAC_SHA384) hmac_b64_digest_len = SHA384_B64_LEN; else if(ctx->hmac_type == FKO_HMAC_SHA512) hmac_b64_digest_len = SHA512_B64_LEN; else return(FKO_ERROR_UNSUPPORTED_HMAC_MODE); if((ctx->encrypted_msg_len - hmac_b64_digest_len) < MIN_SPA_ENCODED_MSG_SIZE) return(FKO_ERROR_INVALID_DATA_HMAC_ENCMSGLEN_VALIDFAIL); /* Get digest value */ hmac_digest_from_data = strndup((ctx->encrypted_msg + ctx->encrypted_msg_len - hmac_b64_digest_len), hmac_b64_digest_len); if(hmac_digest_from_data == NULL) return(FKO_ERROR_MEMORY_ALLOCATION); /* Now we chop the HMAC digest off of the encrypted msg */ tbuf = strndup(ctx->encrypted_msg, ctx->encrypted_msg_len - hmac_b64_digest_len); if(tbuf == NULL) { if(zero_free(hmac_digest_from_data, strnlen(hmac_digest_from_data, MAX_SPA_ENCODED_MSG_SIZE)) == FKO_SUCCESS) return(FKO_ERROR_MEMORY_ALLOCATION); else return(FKO_ERROR_ZERO_OUT_DATA); } if(zero_free(ctx->encrypted_msg, ctx->encrypted_msg_len) != FKO_SUCCESS) zero_free_rv = FKO_ERROR_ZERO_OUT_DATA; ctx->encrypted_msg = tbuf; ctx->encrypted_msg_len -= hmac_b64_digest_len; if(ctx->encryption_mode == FKO_ENC_MODE_ASYMMETRIC) { /* See if we need to add the "hQ" string to the front of the * encrypted data. */ if(! ctx->added_gpg_prefix) { res = add_gpg_prefix(ctx); } } else { /* See if we need to add the "Salted__" string to the front of the * encrypted data. */ if(! ctx->added_salted_str) { res = add_salted_str(ctx); } } if (res != FKO_SUCCESS) { if(zero_free(hmac_digest_from_data, strnlen(hmac_digest_from_data, MAX_SPA_ENCODED_MSG_SIZE)) != FKO_SUCCESS) zero_free_rv = FKO_ERROR_ZERO_OUT_DATA; if(zero_free_rv == FKO_SUCCESS) return(res); else return(zero_free_rv); } /* Calculate the HMAC from the encrypted data and then * compare */ res = fko_set_spa_hmac_type(ctx, ctx->hmac_type); if(res == FKO_SUCCESS) { res = fko_set_spa_hmac(ctx, hmac_key, hmac_key_len); if(res == FKO_SUCCESS) { if(constant_runtime_cmp(hmac_digest_from_data, ctx->msg_hmac, hmac_b64_digest_len) != 0) { res = FKO_ERROR_INVALID_DATA_HMAC_COMPAREFAIL; } } } if(zero_free(hmac_digest_from_data, strnlen(hmac_digest_from_data, MAX_SPA_ENCODED_MSG_SIZE)) != FKO_SUCCESS) zero_free_rv = FKO_ERROR_ZERO_OUT_DATA; if(res == FKO_SUCCESS) return(zero_free_rv); else return(res); }
static void find_domain_master_name_query_success(struct subnet_record *subrec, struct userdata_struct *userdata_in, struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec) { /* * Unfortunately, finding the IP address of the Domain Master Browser, * as we have here, is not enough. We need to now do a sync to the * SERVERNAME<0x20> NetBIOS name, as only recent NT servers will * respond to the SMBSERVER name. To get this name from IP * address we do a Node status request, and look for the first * NAME<0x20> in the response, and take that as the server name. * We also keep a cache of the Domain Master Browser name for this * workgroup in the Workgroup struct, so that if the same IP addess * is returned every time, we don't need to do the node status * request. */ struct work_record *work; struct nmb_name nmbname; struct userdata_struct *userdata; int size = sizeof(struct userdata_struct) + sizeof(fstring)+1; if( !(work = find_workgroup_on_subnet(subrec, q_name->name)) ) { if( DEBUGLVL( 0 ) ) { dbgtext( "find_domain_master_name_query_success:\n" ); dbgtext( "Failed to find workgroup %s\n", q_name->name ); } return; } /* First check if we already have a dmb for this workgroup. */ if(!is_zero_ip(work->dmb_addr) && ip_equal(work->dmb_addr, answer_ip)) { /* Do the local master browser announcement to the domain master browser name and IP. */ announce_local_master_browser_to_domain_master_browser( work ); /* Now synchronise lists with the domain master browser. */ sync_with_dmb(work); return; } else zero_ip(&work->dmb_addr); /* Now initiate the node status request. */ make_nmb_name(&nmbname,"*",0x0); /* Put the workgroup name into the userdata so we know what workgroup we're talking to when the reply comes back. */ /* Setup the userdata_struct - this is copied so we can use a stack variable for this. */ if((userdata = (struct userdata_struct *)malloc(size)) == NULL) { DEBUG(0, ("find_domain_master_name_query_success: malloc fail.\n")); return; } userdata->copy_fn = NULL; userdata->free_fn = NULL; userdata->userdata_len = strlen(work->work_group)+1; pstrcpy(userdata->data, work->work_group); node_status( subrec, &nmbname, answer_ip, domain_master_node_status_success, domain_master_node_status_fail, userdata); zero_free(userdata, size); }
/* Prep and encrypt using Rijndael */ static int _rijndael_encrypt(fko_ctx_t ctx, const char *enc_key, const int enc_key_len) { char *plaintext; char *b64ciphertext; unsigned char *ciphertext; int cipher_len; int pt_len; int zero_free_rv = FKO_SUCCESS; if(enc_key_len > RIJNDAEL_MAX_KEYSIZE) return(FKO_ERROR_INVALID_KEY_LEN); if (! is_valid_encoded_msg_len(ctx->encoded_msg_len)) return(FKO_ERROR_INVALID_DATA_ENCRYPT_MSGLEN_VALIDFAIL); switch(ctx->digest_len) { case MD5_B64_LEN: break; case SHA1_B64_LEN: break; case SHA256_B64_LEN: break; case SHA384_B64_LEN: break; case SHA512_B64_LEN: break; default: return(FKO_ERROR_INVALID_DATA_ENCRYPT_DIGESTLEN_VALIDFAIL); } pt_len = ctx->encoded_msg_len + ctx->digest_len + RIJNDAEL_BLOCKSIZE + 2; /* Make a bucket big enough to hold the enc msg + digest (plaintext) * and populate it appropriately. */ plaintext = calloc(1, pt_len); if(plaintext == NULL) return(FKO_ERROR_MEMORY_ALLOCATION); pt_len = snprintf(plaintext, pt_len, "%s:%s", ctx->encoded_msg, ctx->digest); if(! is_valid_pt_msg_len(pt_len)) { if(zero_free(plaintext, pt_len) == FKO_SUCCESS) return(FKO_ERROR_INVALID_DATA_ENCRYPT_PTLEN_VALIDFAIL); else return(FKO_ERROR_ZERO_OUT_DATA); } /* Make a bucket for the encrypted version and populate it. */ ciphertext = calloc(1, pt_len + 32); /* Plus padding for salt and Block */ if(ciphertext == NULL) { if(zero_free(plaintext, pt_len) == FKO_SUCCESS) return(FKO_ERROR_MEMORY_ALLOCATION); else return(FKO_ERROR_ZERO_OUT_DATA); } cipher_len = rij_encrypt( (unsigned char*)plaintext, pt_len, (char*)enc_key, enc_key_len, ciphertext, ctx->encryption_mode ); /* Now make a bucket for the base64-encoded version and populate it. */ b64ciphertext = malloc(((cipher_len / 3) * 4) + 8); if(b64ciphertext == NULL) { if(zero_free((char *) ciphertext, pt_len+32) == FKO_SUCCESS && zero_free(plaintext, pt_len) == FKO_SUCCESS) return(FKO_ERROR_MEMORY_ALLOCATION); else return(FKO_ERROR_ZERO_OUT_DATA); } b64_encode(ciphertext, b64ciphertext, cipher_len); strip_b64_eq(b64ciphertext); if(ctx->encrypted_msg != NULL) zero_free_rv = zero_free(ctx->encrypted_msg, strnlen(ctx->encrypted_msg, MAX_SPA_ENCODED_MSG_SIZE)); ctx->encrypted_msg = strdup(b64ciphertext); ctx->encrypted_msg_len = strnlen(ctx->encrypted_msg, MAX_SPA_ENCODED_MSG_SIZE); /* Clean-up */ if(zero_free(plaintext, pt_len) != FKO_SUCCESS) zero_free_rv = FKO_ERROR_ZERO_OUT_DATA; if(zero_free((char *) ciphertext, pt_len+32) != FKO_SUCCESS) zero_free_rv = FKO_ERROR_ZERO_OUT_DATA; if(zero_free(b64ciphertext, strnlen(b64ciphertext, MAX_SPA_ENCODED_MSG_SIZE)) != FKO_SUCCESS) zero_free_rv = FKO_ERROR_ZERO_OUT_DATA; if(ctx->encrypted_msg == NULL) return(FKO_ERROR_MEMORY_ALLOCATION); if(! is_valid_encoded_msg_len(ctx->encrypted_msg_len)) return(FKO_ERROR_INVALID_DATA_ENCRYPT_RESULT_MSGLEN_VALIDFAIL); return(zero_free_rv); }
/* Prep and decrypt using gpgme */ static int gpg_decrypt(fko_ctx_t ctx, const char *dec_key) { unsigned char *cipher; size_t cipher_len; int res, pt_len, b64_decode_len; /* Now see if we need to add the "hQ" string to the front of the * base64-encoded-GPG-encrypted data. */ if(! ctx->added_gpg_prefix) add_gpg_prefix(ctx); /* Create a bucket for the (base64) decoded encrypted data and get the * raw cipher data. */ cipher = malloc(ctx->encrypted_msg_len); if(cipher == NULL) return(FKO_ERROR_MEMORY_ALLOCATION); if((b64_decode_len = b64_decode(ctx->encrypted_msg, cipher)) < 0) { if(zero_free((char *) cipher, ctx->encrypted_msg_len) == FKO_SUCCESS) return(FKO_ERROR_INVALID_DATA_ENCRYPT_GPG_CIPHER_DECODEFAIL); else return(FKO_ERROR_ZERO_OUT_DATA); } cipher_len = b64_decode_len; /* Create a bucket for the plaintext data and decrypt the message * data into it. */ /* --DSS Actually, the needed memory will be malloced in the gpgme_decrypt // function. Just leaving this here for reference (for now). //ctx->encoded_msg = malloc(cipher_len); //if(ctx->encoded_msg == NULL) // return(FKO_ERROR_MEMORY_ALLOCATION); */ res = gpgme_decrypt(ctx, cipher, cipher_len, dec_key, (unsigned char**)&ctx->encoded_msg, &cipher_len ); /* Done with cipher... */ if(res != FKO_SUCCESS) { if(zero_free((char *) cipher, ctx->encrypted_msg_len) != FKO_SUCCESS) return(FKO_ERROR_ZERO_OUT_DATA); else return(res); } pt_len = strnlen(ctx->encoded_msg, MAX_SPA_ENCODED_MSG_SIZE); if(ctx->encoded_msg == NULL) return(FKO_ERROR_INVALID_DATA_ENCRYPT_DECRYPTED_MESSAGE_MISSING); if(! is_valid_encoded_msg_len(pt_len)) return(FKO_ERROR_INVALID_DATA_ENCRYPT_DECRYPTED_MSGLEN_VALIDFAIL); ctx->encoded_msg_len = pt_len; /* Call fko_decode and return the results. */ return(fko_decode_spa_data(ctx)); }
/* Prep and encrypt using gpgme */ static int gpg_encrypt(fko_ctx_t ctx, const char *enc_key) { int res; char *plain; int pt_len, zero_free_rv = FKO_SUCCESS; char *b64cipher; unsigned char *cipher = NULL; size_t cipher_len; char *empty_key = ""; if (! is_valid_encoded_msg_len(ctx->encoded_msg_len)) return(FKO_ERROR_INVALID_DATA_ENCRYPT_GPG_MESSAGE_VALIDFAIL); switch(ctx->digest_len) { case MD5_B64_LEN: break; case SHA1_B64_LEN: break; case SHA256_B64_LEN: break; case SHA384_B64_LEN: break; case SHA512_B64_LEN: break; default: return(FKO_ERROR_INVALID_DATA_ENCRYPT_GPG_DIGEST_VALIDFAIL); } /* First make sure we have a recipient key set. */ if(ctx->gpg_recipient == NULL) return(FKO_ERROR_MISSING_GPG_KEY_DATA); pt_len = ctx->encoded_msg_len + ctx->digest_len + 2; /* Make a bucket big enough to hold the enc msg + digest (plaintext) * and populate it appropriately. */ plain = malloc(ctx->encoded_msg_len + ctx->digest_len + 2); if(plain == NULL) return(FKO_ERROR_MEMORY_ALLOCATION); pt_len = snprintf(plain, pt_len+1, "%s:%s", ctx->encoded_msg, ctx->digest); if(! is_valid_pt_msg_len(pt_len)) { if(zero_free(plain, pt_len) == FKO_SUCCESS) return(FKO_ERROR_INVALID_DATA_ENCRYPT_GPG_MSGLEN_VALIDFAIL); else return(FKO_ERROR_ZERO_OUT_DATA); } if (enc_key != NULL) { res = gpgme_encrypt(ctx, (unsigned char*)plain, pt_len, enc_key, &cipher, &cipher_len ); } else { res = gpgme_encrypt(ctx, (unsigned char*)plain, pt_len, empty_key, &cipher, &cipher_len ); } /* --DSS XXX: Better parsing of what went wrong would be nice :) */ if(res != FKO_SUCCESS) { zero_free_rv = zero_free(plain, pt_len); if(cipher != NULL) if(zero_free((char *) cipher, cipher_len) != FKO_SUCCESS) zero_free_rv = FKO_ERROR_ZERO_OUT_DATA; if(zero_free_rv == FKO_SUCCESS) return(res); else return(zero_free_rv); } /* Now make a bucket for the base64-encoded version and populate it. */ b64cipher = malloc(((cipher_len / 3) * 4) + 8); if(b64cipher == NULL) { if(zero_free(plain, pt_len) != FKO_SUCCESS) zero_free_rv = FKO_ERROR_ZERO_OUT_DATA; if(cipher != NULL) if(zero_free((char *) cipher, cipher_len) != FKO_SUCCESS) zero_free_rv = FKO_ERROR_ZERO_OUT_DATA; if(zero_free_rv == FKO_SUCCESS) return(FKO_ERROR_MEMORY_ALLOCATION); else return(zero_free_rv); } b64_encode(cipher, b64cipher, cipher_len); strip_b64_eq(b64cipher); if(ctx->encrypted_msg != NULL) zero_free_rv = zero_free(ctx->encrypted_msg, strnlen(ctx->encrypted_msg, MAX_SPA_ENCODED_MSG_SIZE)); ctx->encrypted_msg = strdup(b64cipher); ctx->encrypted_msg_len = strnlen(ctx->encrypted_msg, MAX_SPA_ENCODED_MSG_SIZE); /* Clean-up */ if(zero_free(plain, pt_len) != FKO_SUCCESS) zero_free_rv = FKO_ERROR_ZERO_OUT_DATA; if(zero_free((char *) cipher, cipher_len) != FKO_SUCCESS) zero_free_rv = FKO_ERROR_ZERO_OUT_DATA; if(zero_free(b64cipher, strnlen(b64cipher, MAX_SPA_ENCODED_MSG_SIZE)) != FKO_SUCCESS) zero_free_rv = FKO_ERROR_ZERO_OUT_DATA; if(ctx->encrypted_msg == NULL) return(FKO_ERROR_MEMORY_ALLOCATION); if(! is_valid_encoded_msg_len(ctx->encrypted_msg_len)) return(FKO_ERROR_INVALID_DATA_ENCRYPT_GPG_RESULT_MSGLEN_VALIDFAIL); return(zero_free_rv); }
/* Decode, decrypt, and parse SPA data into the context. */ static int _rijndael_decrypt(fko_ctx_t ctx, const char *dec_key, const int key_len, int encryption_mode) { unsigned char *ndx; unsigned char *cipher; int cipher_len, pt_len, i, err = 0, res = FKO_SUCCESS; int zero_free_rv = FKO_SUCCESS; if(key_len > RIJNDAEL_MAX_KEYSIZE) return(FKO_ERROR_INVALID_KEY_LEN); /* Now see if we need to add the "Salted__" string to the front of the * encrypted data. */ if(! ctx->added_salted_str) { res = add_salted_str(ctx); if(res != FKO_SUCCESS) return res; } /* Create a bucket for the (base64) decoded encrypted data and get the * raw cipher data. */ cipher = malloc(ctx->encrypted_msg_len); if(cipher == NULL) return(FKO_ERROR_MEMORY_ALLOCATION); if((cipher_len = b64_decode(ctx->encrypted_msg, cipher)) < 0) { if(zero_free((char *)cipher, ctx->encrypted_msg_len) == FKO_SUCCESS) return(FKO_ERROR_INVALID_DATA_ENCRYPT_CIPHERLEN_DECODEFAIL); else return(FKO_ERROR_ZERO_OUT_DATA); } /* Since we're using AES, make sure the incoming data is a multiple of * the blocksize */ if((cipher_len % RIJNDAEL_BLOCKSIZE) != 0) { if(zero_free((char *)cipher, ctx->encrypted_msg_len) == FKO_SUCCESS) return(FKO_ERROR_INVALID_DATA_ENCRYPT_CIPHERLEN_VALIDFAIL); else return(FKO_ERROR_ZERO_OUT_DATA); } if(ctx->encoded_msg != NULL) zero_free_rv = zero_free(ctx->encoded_msg, strnlen(ctx->encoded_msg, MAX_SPA_ENCODED_MSG_SIZE)); /* Create a bucket for the plaintext data and decrypt the message * data into it. */ ctx->encoded_msg = malloc(cipher_len); if(ctx->encoded_msg == NULL) { if(zero_free((char *)cipher, ctx->encrypted_msg_len) == FKO_SUCCESS) return(FKO_ERROR_MEMORY_ALLOCATION); else return(FKO_ERROR_ZERO_OUT_DATA); } pt_len = rij_decrypt(cipher, cipher_len, dec_key, key_len, (unsigned char*)ctx->encoded_msg, encryption_mode); /* Done with cipher... */ if(zero_free((char *)cipher, ctx->encrypted_msg_len) != FKO_SUCCESS) zero_free_rv = FKO_ERROR_ZERO_OUT_DATA; /* The length of the decrypted data should be within 32 bytes of the * length of the encrypted version. */ if(pt_len < (cipher_len - 32) || pt_len <= 0) return(FKO_ERROR_DECRYPTION_SIZE); if(ctx->encoded_msg == NULL) return(FKO_ERROR_INVALID_DATA_ENCRYPT_GPG_ENCODEDMSG_NULL); if(! is_valid_encoded_msg_len(pt_len)) return(FKO_ERROR_INVALID_DATA_ENCRYPT_GPG_ENCODEDMSGLEN_VALIDFAIL); if(zero_free_rv != FKO_SUCCESS) return(zero_free_rv); ctx->encoded_msg_len = pt_len; /* At this point we can check the data to see if we have a good * decryption by ensuring the first field (16-digit random decimal * value) is valid and is followed by a colon. Additional checks * are made in fko_decode_spa_data(). */ ndx = (unsigned char *)ctx->encoded_msg; for(i=0; i<FKO_RAND_VAL_SIZE; i++) if(!isdigit(*(ndx++))) err++; if(err > 0 || *ndx != ':') return(FKO_ERROR_DECRYPTION_FAILURE); /* Call fko_decode and return the results. */ return(fko_decode_spa_data(ctx)); }
/* Destroy a context and free its resources */ int fko_destroy(fko_ctx_t ctx) { int zero_free_rv = FKO_SUCCESS; #if HAVE_LIBGPGME fko_gpg_sig_t gsig, tgsig; #endif if(!CTX_INITIALIZED(ctx)) return(zero_free_rv); if(ctx->rand_val != NULL) free(ctx->rand_val); if(ctx->username != NULL) free(ctx->username); if(ctx->version != NULL) free(ctx->version); if(ctx->message != NULL) free(ctx->message); if(ctx->nat_access != NULL) free(ctx->nat_access); if(ctx->server_auth != NULL) free(ctx->server_auth); if(ctx->digest != NULL) if(zero_free(ctx->digest, ctx->digest_len) != FKO_SUCCESS) zero_free_rv = FKO_ERROR_ZERO_OUT_DATA; if(ctx->raw_digest != NULL) if(zero_free(ctx->raw_digest, ctx->raw_digest_len) != FKO_SUCCESS) zero_free_rv = FKO_ERROR_ZERO_OUT_DATA; if(ctx->encoded_msg != NULL) if(zero_free(ctx->encoded_msg, ctx->encoded_msg_len) != FKO_SUCCESS) zero_free_rv = FKO_ERROR_ZERO_OUT_DATA; if(ctx->encrypted_msg != NULL) if(zero_free(ctx->encrypted_msg, ctx->encrypted_msg_len) != FKO_SUCCESS) zero_free_rv = FKO_ERROR_ZERO_OUT_DATA; if(ctx->msg_hmac != NULL) if(zero_free(ctx->msg_hmac, ctx->msg_hmac_len) != FKO_SUCCESS) zero_free_rv = FKO_ERROR_ZERO_OUT_DATA; #if HAVE_LIBGPGME if(ctx->gpg_exe != NULL) free(ctx->gpg_exe); if(ctx->gpg_home_dir != NULL) free(ctx->gpg_home_dir); if(ctx->gpg_recipient != NULL) free(ctx->gpg_recipient); if(ctx->gpg_signer != NULL) free(ctx->gpg_signer); if(ctx->recipient_key != NULL) gpgme_key_unref(ctx->recipient_key); if(ctx->signer_key != NULL) gpgme_key_unref(ctx->signer_key); if(ctx->gpg_ctx != NULL) gpgme_release(ctx->gpg_ctx); gsig = ctx->gpg_sigs; while(gsig != NULL) { if(gsig->fpr != NULL) free(gsig->fpr); tgsig = gsig; gsig = gsig->next; free(tgsig); } #endif /* HAVE_LIBGPGME */ memset(ctx, 0x0, sizeof(*ctx)); free(ctx); return(zero_free_rv); }