Hacker for Hire

Storing PKCS#12 Using the OpenLDAP C API

Wyatt • • Rants and Technology

First off, I’m sorry it’s been a while since I’ve updated. I’ve been insane busy fighting the battles that need to be fought at the house; however, I do want to document this since I can’t seem to find how to do it anywhere else on the Internet and I know that I can’t be the only other person in the world that would ever want to store a PKCS#12 certificate in OpenLDAP using C. It took a little longer because I wanted nice clean sections of code for people to look at. Kudos to the Code Snippet [direct download] plugin for making that happen, originally found at http://blog.enargi.com/codesnippet/. (The guys blog is down and has been for some time … I though I’d make it available to anyone else who wanted this kick-ass plugin.)

First a little background information on the “userPKCS12″ object in the schema. I quote directly:
PKCS #12 [PKCS12] provides a format for exchange of personal identity information. When such information is stored in a directory service, the userPKCS12 attribute should be used. This attribute is to be stored and requested in binary form, as ‘userPKCS12;binary’. The attribute values are PFX PDUs stored as binary data. OpenLDAP note: “;binary” transfer should NOT be used as syntax is binary

Now does anyone else think that it’s really stupid that the file is STORED and REQUESTED using the “;binary” transport syntax, but that you aren’t allowed to use it? If you do a Google on this, you’ll find a nice little mail message about someone who was trying to overcome this issue but never said how he did it … and that’s what I’m here to fix.

Stupid! Stupid! Stupid!
In my search for how to pull this off, I found tons and tons of the exact information that didn’t really get you anywhere. Here are 2 myths that everyone seems to believe, but aren’t really documented as inaccurate anywhere:

1. userPKCS12 must be Base64 encoded before you send them to LDAP
Err! Wrong! userPKCS12 files must be Base64 encoded IF you are using an LDIF file. Since we are using the API in C, this is a load of horse crap.

2. userPKCS12 must be transported in UFT-8 format
Wrong again! If you were again using the OpenLDAP tools, yes, then you can encode the Base64/userPKCS12 (I’m not sure which, ever time I asked that I was given a bunch of crap about how I should use use the OpenLDAP tools and not write my own). The purpose is to remove the invalid binary characters that occur when you want to use something like ldap://blah.com:389.

It doesn’t work
Since I love STL and their strings, that’s what I stored my userPKCS12 data in, which worked really well. I could access anything I needed, decrypt the information, pull out PEMs using OpenSSL, etc. OpenLDAP was the only bastard that wouldn’t work. Since there are a ton of great documents out there on how to use ldap_modify_ext_s and actually initialize the OpenLDAP library (although 2.3, all the previous functions were deprecated so you have to use all the undocumented ones that are located in the ldap.h header file … FIX YOUR DAMNED DOCS!!!), I’m going to assume you already have done that and we are merely setting up the LDAPMod*[] array that send to OpenLDAP … that’s where the problem was anyway.

Here’s the first pass that didn’t have a snowball’s chance in hell:

string strUserPKCS12 = binaryPKCS12Cert;
LDAPMod *modarray[2];

modarray[] = new LDAPMod;

modarray[]->mod_op = LDAP_MOD_REPLACE | LDAP_MOD_ADD;
modarray[]->mod_type = "“userPKCS12";
modarray[]->mod_values = strdup( (char*) strUserPKCS12.c_str() );
modarray[1] = NULL;

The problem with this code was that calling .c_str() might return the whole string, but that we are working with a very long segment of binary data (4k of data almost) so it might not really do what our orignal purpose was. If you sit back and look at it, this is really a stupid move on my part because strdup() will stop right after the first NULL it finds. It also failed because we were using the wrong LDAPMod structure value. This was the second pass to try and fix those issues:

string strUserPKCS12 = binaryPKCS12Cert;
LDAPMod *modarray[2];
struct berval *pointerArray[2];
struct berval *binaryData = new struct berval;

modarray[] = new LDAPMod;
modarray[]->mod_op = LDAP_MOD_REPLACE | LDAP_MOD_ADD;
modarray[]->mod_type = "“userPKCS12";

binaryData->bv_len = strUserPKCS12.size();
binaryData->bv_val = (char *) strUserPKCS12.data();

pointerArray[] = binaryData;
pointerArray[1] = NULL;

modarray[]->mod_bvalues = pointerArray;
modarray[1] = NULL;

The problem here took me for flipping ever to find. Normally, when you specify a size and a pointer to the data segment, it should just be like “Oh sure, I can handle that.”; however, ber_printf in the OpenLDAP code actually eats the memory in a really stupid fashion. This was tired with .c_str() and .data() which both work with OpenSSL, yet not with OpenLDAP. The reason it took me so long to find was that it was eating the memory near the middle 3.5k mark and leaving the end intact and my debugger wasn’t showing me that. So what was the solution? I’m not going to tell you … ha, I kid … here it is.

string strUserPKCS12 = binaryPKCS12Cert;
LDAPMod *modarray[2];
struct berval *pointerArray[2];
struct berval *binaryData = new struct berval;

modarray[] = new LDAPMod;
modarray[]->mod_op = LDAP_MOD_REPLACE | LDAP_MOD_ADD;
modarray[]->mod_type = "userPKCS12";

char *myUserPKCS12 = (char *) malloc (sizeof(char) * strUserPKCS12.size());
memset(myUserPKCS12, , strUserPKCS12.size());
memcpy(myUserPKCS12,strUserPKCS12.data(),strUserPKCS12.size());
binaryData->bv_len = strUserPKCS12.size();
binaryData->bv_val = myUserPKCS12;

pointerArray[] = binaryData;
pointerArray[1] = NULL;

modarray[]->mod_bvalues = pointerArray;
modarray[1] = NULL;

Programmatically, this code is exactly the same as the code above with the difference that I had to malloc() my own space even though the STL string was supposed to give me a pointer to the direct data segment (which it does, OpenLDAP just clobbers it in the ber_printf() call) and I specified exactly how much data was supposed to get set into my array. Looking back I realize how this is my fault for relying on some shoddy programming tactics and that strdup() stops at null style characters; however, that still doesn’t excuse the fact that there’s a lack of a document on this massive fricken Internet that tells you how to make this magic happen. Don’t forget to free your memory so you don’t look like a crappy OpenLDAP programmer :-).

comments powered by Disqus