| 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 |
|
|---|
| 19 | extern char *convert_wstring(const wchar_t *);
|
|---|
| 20 | extern wchar_t *convert_mbstring(const char *);
|
|---|
| 21 |
|
|---|
| 22 | extern PORANGEFS_OPTIONS goptions;
|
|---|
| 23 |
|
|---|
| 24 | /* initialize OpenSSL */
|
|---|
| 25 | void 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 */
|
|---|
| 34 | void 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) */
|
|---|
| 42 | static 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 |
|
|---|
| 62 | static 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 */
|
|---|
| 81 | static 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 |
|
|---|
| 132 | static 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 */
|
|---|
| 175 | static 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 |
|
|---|
| 213 | verify_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 */
|
|---|
| 231 | static 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 |
|
|---|
| 269 | static 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 */
|
|---|
| 281 | int 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 |
|
|---|
| 385 | get_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 | }
|
|---|