root/branches/windows-client/src/client/windows/client-service/cert.c @ 8840

Revision 8840, 9.7 KB (checked in by sampson, 2 years ago)

Windows certificate support

  • Property svn:executable set to *
Line 
1/* Copyright (C) 2011 Omnibond LLC
2   Certificate functions */
3
4#include <Windows.h>
5#include <LM.h>
6#include <stdio.h>
7
8#include <openssl/ssl.h>
9#include <openssl/bio.h>
10#include <openssl/pem.h>
11#include <openssl/err.h>
12#include <openssl/x509.h>
13#include <openssl/x509v3.h>
14#include <openssl/x509_vfy.h>
15
16#include "cert.h"
17#include "user-cache.h"
18
19extern char *convert_wstring(const wchar_t *);
20extern wchar_t *convert_mbstring(const char *);
21
22extern PORANGEFS_OPTIONS goptions;
23
24/* initialize OpenSSL */
25void openssl_init()
26{
27    SSL_library_init();
28    SSL_load_error_strings();
29    ERR_load_BIO_strings();
30    OpenSSL_add_all_algorithms();
31}
32
33/* cleanup OpenSSL */
34void openssl_cleanup()
35{
36    CRYPTO_cleanup_all_ex_data();
37    ERR_free_strings();
38    ERR_remove_state(0);
39}
40
41/* load certificate from file (PEM format) */
42static unsigned long load_cert_from_file(char *path,
43                                         X509 **cert)
44{
45    FILE *f;
46
47    if (path == NULL || cert == NULL)
48        return -1;
49
50    f = fopen(path, "r");
51    if (f == NULL)
52        return errno;
53
54    *cert = PEM_read_X509(f, NULL, NULL, NULL);
55    if (cert == NULL)
56        return ERR_get_error();
57
58    return 0;
59}
60
61
62static int get_proxy_auth_ex_data_idx(void)
63{
64    static volatile int idx = -1;
65    if (idx < 0)
66    {
67        CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
68        if (idx < 0)
69        {
70            idx = X509_STORE_CTX_get_ex_new_index(0,
71                                                  "for verify callback",
72                                                  NULL,NULL,NULL);
73        }
74        CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
75    }
76
77    return idx;
78}
79
80/* parse the credential string uid/gid from credstr */
81static int parse_credentials(char *credstr, PVFS_uid *uid, PVFS_gid *gid)
82{
83    char *p, uidstr[16], gidstr[16];
84    int i, ret = 0;
85
86    uidstr[0] = gidstr[0] = '\0';
87    i = 0;
88    p = credstr;
89    while (*p && *p != '/' && i < 15)
90    {
91        if (isdigit(*p))
92        {
93            uidstr[i++] = *p++;
94        }
95        else
96        {
97            /* error */
98            ret = 1;
99            break;
100        }
101    }
102    uidstr[i] = '\0';
103    if (ret == 0)
104    {
105        if (*p == '/')
106            p++;
107        i = 0;
108        while(*p && i < 15)
109        {
110            if (isdigit(*p))
111            {
112                gidstr[i++] = *p++;
113            }
114            else
115            {
116                ret = 1;
117                break;
118            }
119        }
120        gidstr[i] = '\0';
121    }
122
123    if (ret == 0)
124    {
125        *uid = atoi(uidstr);
126        *gid = atoi(gidstr);
127    }
128
129    return ret;
130}
131
132static int verify_callback(int ok, X509_STORE_CTX *ctx)
133{
134    X509 *xs;
135    PROXY_CERT_INFO_EXTENSION *pci;
136    char *credstr;
137    PVFS_credentials *credentials;
138    int ret;
139
140    /* prior verifies have succeeded */
141    if (ok == 1)
142    {
143        /* parse the credential string uid/gid from the policy */
144        xs = ctx->current_cert;
145        if (xs->ex_flags & EXFLAG_PROXY)
146        {
147            pci = (PROXY_CERT_INFO_EXTENSION *)
148                    X509_get_ext_d2i(xs, NID_proxyCertInfo, NULL, NULL);
149
150            credstr = (char *) pci->proxyPolicy->policy->data;
151            if (pci->proxyPolicy->policy->length > 0)
152            {
153                credentials = (PVFS_credentials *) X509_STORE_CTX_get_ex_data(
154                                 ctx, get_proxy_auth_ex_data_idx());
155                ret = parse_credentials(credstr, &credentials->uid,
156                                        &credentials->gid);
157                if (ret != 0)
158                {
159                    DbgPrint("Could not parse credential string: %s\n", credstr);
160                }
161            }
162            else
163            {
164                DbgPrint("Could not load policy\n");
165            }
166
167            PROXY_CERT_INFO_EXTENSION_free(pci);
168        }
169    }
170   
171    return ok;
172}
173
174/* verify certificate */
175static unsigned long verify_cert(X509 *cert,
176                                 X509 *ca_cert,
177                                 STACK_OF(X509) *chain,
178                                 PVFS_credentials *credentials)
179{
180    X509_STORE *trust_store;
181    X509_STORE_CTX *ctx;
182    int ret;
183    unsigned long err;
184    int (*save_verify_cb)(int ok, X509_STORE_CTX *ctx);
185
186    /* add CA cert to trusted store */
187    trust_store = X509_STORE_new();
188    if (trust_store == NULL)
189        goto verify_cert_exit;
190
191    ret = X509_STORE_add_cert(trust_store, ca_cert);
192    if (!ret)
193        goto verify_cert_exit;
194
195    /* setup the context with the certs */
196    ctx = X509_STORE_CTX_new();
197    if (ctx == NULL)
198        goto verify_cert_exit;
199
200    ret = X509_STORE_CTX_init(ctx, trust_store, cert, chain);
201    if (!ret)
202        goto verify_cert_exit;
203
204    /* verify the cert and get credentials */
205    save_verify_cb = ctx->verify_cb;
206    X509_STORE_CTX_set_verify_cb(ctx, verify_callback);
207    X509_STORE_CTX_set_ex_data(ctx, get_proxy_auth_ex_data_idx(), credentials);
208    X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_ALLOW_PROXY_CERTS);
209    ret = X509_verify_cert(ctx);
210
211    X509_STORE_CTX_set_verify_cb(ctx, save_verify_cb);
212   
213verify_cert_exit:
214    err = ERR_get_error();
215
216    if (ctx != NULL)
217    {
218        X509_STORE_CTX_cleanup(ctx);
219        X509_STORE_CTX_free(ctx);
220    }
221
222    if (trust_store != NULL)
223    {
224        X509_STORE_free(trust_store);
225    }
226
227    return err;
228}
229
230/* get user home directory */
231static unsigned int get_home_dir(char *userid,
232                                 char *home_dir)
233{
234    LPUSER_INFO_11 user_info;
235    LPWSTR wuserid;
236    int ret;
237    char *mbstr;
238
239    /* convert to unicode */
240    wuserid = convert_mbstring(userid);
241    if (wuserid == NULL)
242        return -1;
243
244    /* get user information */
245    ret = NetUserGetInfo(NULL, wuserid, 11, (LPBYTE *) &user_info);
246
247    if (ret == 0)
248    {
249        mbstr = convert_wstring(user_info->usri11_home_dir);
250        if (mbstr == NULL)
251        {
252            free(wuserid);
253            ret = -1;
254        }
255       
256        strncpy(home_dir, mbstr, MAX_PATH);
257
258        if (mbstr != NULL)
259            free(mbstr);
260
261        NetApiBufferFree(user_info);
262    }
263
264    free(wuserid);
265
266    return ret;
267}
268
269static time_t get_cert_expires(X509 *cert)
270{
271    ASN1_INTEGER *asn1_int;
272
273    asn1_int = X509_get_notAfter(cert);
274    if (asn1_int != NULL)
275        return ASN1_INTEGER_get(asn1_int);
276
277    return 0;
278}
279
280/* retrieve OrangeFS credentials from cert */
281int get_cert_credentials(char *userid,
282                         PVFS_credentials *credentials,
283                         time_t *expires)
284{
285    char cert_dir[MAX_PATH], cert_path[MAX_PATH],
286         cert_pattern[MAX_PATH];
287    HANDLE h_find;
288    WIN32_FIND_DATA find_data;
289    X509 *cert = NULL, *chain_cert = NULL, *ca_cert = NULL;
290    STACK_OF(X509) *chain;
291    int ret;
292
293    if (userid == NULL || credentials == NULL)
294        return -1;
295
296    /* locate the certificates and CA */
297    if (strlen(goptions->cert_dir_prefix) > 0)
298    {
299        if ((strlen(goptions->cert_dir_prefix) + strlen(userid) + 8) > MAX_PATH)
300        {
301            DbgPrint("User %s: path to cert too long\n", userid);
302            return -1;
303        }
304
305        /* cert dir is cert_dir_prefix\userid */
306        strcpy(cert_dir, goptions->cert_dir_prefix);
307        strcat(cert_dir, userid);
308    }
309    else
310    {
311        /* get profile directory */
312        ret = get_home_dir(userid, cert_dir);
313        if (ret != 0)
314        {
315            DbgPrint("User %s: could not locate profile dir: %d\n", userid,
316                ret);
317            return ret;
318        }
319       
320        if (strlen(cert_dir) + 7 > MAX_PATH)
321        {
322            DbgPrint("User %s: profile dir too long\n", userid);
323            return -1;
324        }
325    }
326   
327    /* load certs */
328    chain = sk_X509_new_null();
329
330    strcpy(cert_pattern, cert_dir);
331    strcat(cert_pattern, "\\cert.*");
332    h_find = FindFirstFile(cert_pattern, &find_data);
333    if (h_find == INVALID_HANDLE_VALUE)
334    {
335        DbgPrint("User %s: no certificates\n", userid);
336        return -1;
337    }
338
339    do
340    {
341        strcpy(cert_path, cert_dir);
342        strcat(cert_path, find_data.cFileName);
343        /* load proxy cert */
344        if (!stricmp(find_data.cFileName, "cert.0"))
345        {
346            ret = load_cert_from_file(cert_path, &cert);
347        }
348        else
349        {
350            /* load intermediate certs (including user cert) */
351            ret = load_cert_from_file(cert_path, &chain_cert);
352            if (ret == 0)
353                sk_X509_push(chain, chain_cert);
354        }
355        if (ret != 0)
356        {
357            DbgPrint("Error loading cert %s: %d\n", cert_path, ret);
358        }
359    } while (ret == 0 && FindNextFile(h_find, &find_data));
360
361    FindClose(h_find);
362   
363    if (cert == NULL)
364        ret = -1;
365   
366    if (ret != 0)
367        goto get_cert_credentials_exit;
368
369    /* load CA cert */
370    ret = load_cert_from_file(goptions->ca_path, &ca_cert);
371    if (ret != 0)
372    {
373        DbgPrint("Error loading CA cert %s: %d\n", cert_path, ret);
374        goto get_cert_credentials_exit;
375    }
376
377    /* read and cache credentials from certificate */
378    ret = verify_cert(cert, ca_cert, chain, credentials);
379
380    if (ret == 0)
381    {
382        *expires = get_cert_expires(cert);
383    }
384
385get_cert_credentials_exit:
386
387    /* free chain */
388    while (sk_X509_num(chain) > 0)
389        sk_X509_pop_free(chain, X509_free);
390    sk_X509_free(chain);
391
392    if (cert != NULL)
393        X509_free(cert);
394    if (ca_cert != NULL)
395        X509_free(ca_cert);
396
397    return ret;
398}
Note: See TracBrowser for help on using the browser.