Home | History | Annotate | Download | only in os
      1 /*
      2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 
      7 /*
      8  * lib/krb5/os/init_ctx.c
      9  *
     10  * Copyright 1994 by the Massachusetts Institute of Technology.
     11  * All Rights Reserved.
     12  *
     13  * Export of this software from the United States of America may
     14  *   require a specific license from the United States Government.
     15  *   It is the responsibility of any person or organization contemplating
     16  *   export to obtain such a license before exporting.
     17  *
     18  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
     19  * distribute this software and its documentation for any purpose and
     20  * without fee is hereby granted, provided that the above copyright
     21  * notice appear in all copies and that both that copyright notice and
     22  * this permission notice appear in supporting documentation, and that
     23  * the name of M.I.T. not be used in advertising or publicity pertaining
     24  * to distribution of the software without specific, written prior
     25  * permission.  Furthermore if you modify this software you must label
     26  * your software as modified software and not distribute it in such a
     27  * fashion that it might be confused with the original M.I.T. software.
     28  * M.I.T. makes no representations about the suitability of
     29  * this software for any purpose.  It is provided "as is" without express
     30  * or implied warranty.
     31  *
     32  * krb5_init_contex()
     33  */
     34 
     35 #define NEED_WINDOWS
     36 
     37 #include "k5-int.h"
     38 #ifndef _KERNEL
     39 #include "os-proto.h"
     40 #if 0 /* Solaris Kerberos */
     41 #include "prof_int.h"		/* XXX for profile_copy, not public yet */
     42 #endif
     43 errcode_t KRB5_CALLCONV profile_copy (profile_t, profile_t *);
     44 #endif
     45 
     46 #ifdef USE_LOGIN_LIBRARY
     47 #include "KerberosLoginPrivate.h"
     48 #endif
     49 
     50 #if defined(_WIN32)
     51 #include <winsock.h>
     52 
     53 static krb5_error_code
     54 get_from_windows_dir(
     55     char **pname
     56     )
     57 {
     58     UINT size = GetWindowsDirectory(0, 0);
     59     *pname = malloc(size + 1 +
     60                     strlen(DEFAULT_PROFILE_FILENAME) + 1);
     61     if (*pname)
     62     {
     63         GetWindowsDirectory(*pname, size);
     64         strcat(*pname, "\\");
     65         strcat(*pname, DEFAULT_PROFILE_FILENAME);
     66         return 0;
     67     } else {
     68         return KRB5_CONFIG_CANTOPEN;
     69     }
     70 }
     71 
     72 static krb5_error_code
     73 get_from_module_dir(
     74     char **pname
     75     )
     76 {
     77     const DWORD size = 1024; /* fixed buffer */
     78     int found = 0;
     79     char *p;
     80     char *name;
     81     struct _stat s;
     82 
     83     *pname = 0;
     84 
     85     name = MALLOC(size);
     86     if (!name)
     87         return ENOMEM;
     88 
     89     if (!GetModuleFileName(GetModuleHandle("krb5_32"), name, size))
     90         goto cleanup;
     91 
     92     p = name + strlen(name);
     93     while ((p >= name) && (*p != '\\') && (*p != '/')) p--;
     94     if (p < name)
     95         goto cleanup;
     96     p++;
     97     strncpy(p, DEFAULT_PROFILE_FILENAME, size - (p - name));
     98     name[size - 1] = 0;
     99     found = !_stat(name, &s);
    100 
    101  cleanup:
    102     if (found)
    103         *pname = name;
    104     else
    105         if (name) FREE(name, size);
    106     return 0;
    107 }
    108 
    109 /*
    110  * get_from_registry
    111  *
    112  * This will find a profile in the registry.  *pbuffer != 0 if we
    113  * found something.  Make sure to free(*pbuffer) when done.  It will
    114  * return an error code if there is an error the user should know
    115  * about.  We maintain the invariant: return value != 0 =>
    116  * *pbuffer == 0.
    117  */
    118 static krb5_error_code
    119 get_from_registry(
    120     char** pbuffer,
    121     HKEY hBaseKey
    122     )
    123 {
    124     HKEY hKey = 0;
    125     LONG rc = 0;
    126     DWORD size = 0;
    127     krb5_error_code retval = 0;
    128     const char *key_path = "Software\\MIT\\Kerberos5";
    129     const char *value_name = "config";
    130 
    131     /* a wannabe assertion */
    132     if (!pbuffer)
    133     {
    134         /*
    135          * We have a programming error!  For now, we segfault :)
    136          * There is no good mechanism to deal.
    137          */
    138     }
    139     *pbuffer = 0;
    140 
    141     if ((rc = RegOpenKeyEx(hBaseKey, key_path, 0, KEY_QUERY_VALUE,
    142                            &hKey)) != ERROR_SUCCESS)
    143     {
    144         /* not a real error */
    145         goto cleanup;
    146     }
    147     rc = RegQueryValueEx(hKey, value_name, 0, 0, 0, &size);
    148     if ((rc != ERROR_SUCCESS) &&  (rc != ERROR_MORE_DATA))
    149     {
    150         /* not a real error */
    151         goto cleanup;
    152     }
    153     *pbuffer = MALLOC(size);
    154     if (!*pbuffer)
    155     {
    156         retval = ENOMEM;
    157         goto cleanup;
    158     }
    159     if ((rc = RegQueryValueEx(hKey, value_name, 0, 0, *pbuffer, &size)) !=
    160         ERROR_SUCCESS)
    161     {
    162         /*
    163          * Let's not call it a real error in case it disappears, but
    164          * we need to free so that we say we did not find anything.
    165          */
    166         FREE(*pbuffer, size);
    167         *pbuffer = 0;
    168         goto cleanup;
    169     }
    170  cleanup:
    171     if (hKey)
    172         RegCloseKey(hKey);
    173     if (retval && *pbuffer)
    174     {
    175         FREE(*pbuffer, size);
    176         /* Let's say we did not find anything: */
    177         *pbuffer = 0;
    178     }
    179     return retval;
    180 }
    181 
    182 #endif /* _WIN32 */
    183 
    184 #ifndef _KERNEL
    185 static void
    186 free_filespecs(profile_filespec_t *files)
    187 {
    188     char **cp;
    189 
    190     if (files == 0)
    191         return;
    192 
    193     for (cp = files; *cp; cp++)
    194 	free(*cp);
    195     free(files);
    196 }
    197 
    198 /* This function is needed by KfM's KerberosPreferences API
    199  * because it needs to be able to specify "secure" */
    200 krb5_error_code
    201 os_get_default_config_files(profile_filespec_t **pfiles, krb5_boolean secure)
    202 {
    203     profile_filespec_t* files;
    204 #if defined(_WIN32)
    205     krb5_error_code retval = 0;
    206     char *name = 0;
    207 
    208     if (!secure)
    209     {
    210         char *env = getenv("KRB5_CONFIG");
    211         if (env)
    212         {
    213             name = malloc(strlen(env) + 1);
    214             if (!name) return ENOMEM;
    215             strcpy(name, env);
    216         }
    217     }
    218     if (!name && !secure)
    219     {
    220         /* HKCU */
    221         retval = get_from_registry(&name, HKEY_CURRENT_USER);
    222         if (retval) return retval;
    223     }
    224     if (!name)
    225     {
    226         /* HKLM */
    227         retval = get_from_registry(&name, HKEY_LOCAL_MACHINE);
    228         if (retval) return retval;
    229     }
    230     if (!name && !secure)
    231     {
    232         /* module dir */
    233         retval = get_from_module_dir(&name);
    234         if (retval) return retval;
    235     }
    236     if (!name)
    237     {
    238         /* windows dir */
    239         retval = get_from_windows_dir(&name);
    240     }
    241     if (retval)
    242         return retval;
    243     if (!name)
    244         return KRB5_CONFIG_CANTOPEN; /* should never happen */
    245 
    246     files = malloc(2 * sizeof(char *));
    247     files[0] = name;
    248     files[1] = 0;
    249 #else /* !_WIN32 */
    250     char* filepath = 0;
    251     int n_entries, i;
    252     unsigned int ent_len;
    253     const char *s, *t;
    254 
    255 #ifdef USE_LOGIN_LIBRARY
    256     /* If __KLAllowHomeDirectoryAccess() == FALSE, we are probably
    257         trying to authenticate to a fileserver for the user's homedir. */
    258     if (!__KLAllowHomeDirectoryAccess ())
    259 	secure = 1;
    260 #endif
    261     if (secure) {
    262 	filepath = DEFAULT_SECURE_PROFILE_PATH;
    263     } else {
    264         filepath = getenv("KRB5_CONFIG");
    265         if (!filepath) filepath = DEFAULT_PROFILE_PATH;
    266     }
    267 
    268     /* count the distinct filename components */
    269     for(s = filepath, n_entries = 1; *s; s++) {
    270         if (*s == ':')
    271             n_entries++;
    272     }
    273 
    274     /* the array is NULL terminated */
    275     files = (char**) MALLOC((n_entries+1) * sizeof(char*));
    276     if (files == 0)
    277         return ENOMEM;
    278 
    279     /* measure, copy, and skip each one */
    280     /*LINTED*/
    281     for(s = filepath, i=0; (t = strchr(s, ':')) || (t=s+strlen(s)); s=t+1, i++)
    282     {
    283         ent_len = t-s;
    284         files[i] = (char*) malloc(ent_len + 1);
    285         if (files[i] == 0) {
    286             /* if malloc fails, free the ones that worked */
    287             while(--i >= 0) free(files[i]);
    288             free(files);
    289             return ENOMEM;
    290         }
    291         strncpy(files[i], s, ent_len);
    292         files[i][ent_len] = 0;
    293         if (*t == 0) {
    294             i++;
    295             break;
    296         }
    297     }
    298     /* cap the array */
    299     files[i] = 0;
    300 #endif /* !_WIN32 */
    301     *pfiles = (profile_filespec_t *)files;
    302     return 0;
    303 }
    304 
    305 static krb5_error_code
    306 add_kdc_config_file(profile_filespec_t **pfiles)
    307 {
    308     char *file;
    309     size_t count;
    310     profile_filespec_t *newfiles;
    311 
    312     file = getenv(KDC_PROFILE_ENV);
    313     if (file == NULL)
    314 	file = DEFAULT_KDC_PROFILE;
    315 
    316     for (count = 0; (*pfiles)[count]; count++)
    317 	;
    318     count += 2;
    319     newfiles = malloc(count * sizeof(*newfiles));
    320     if (newfiles == NULL)
    321 	return errno;
    322     memcpy(newfiles + 1, *pfiles, (count-1) * sizeof(*newfiles));
    323     newfiles[0] = strdup(file);
    324     if (newfiles[0] == NULL) {
    325 	int e = errno;
    326 	free(newfiles);
    327 	return e;
    328     }
    329     free(*pfiles);
    330     *pfiles = newfiles;
    331     return 0;
    332 }
    333 
    334 
    335 /* Set the profile paths in the context.  If secure is set to TRUE
    336    then do not include user paths (from environment variables, etc).
    337    If kdc is TRUE, include kdc.conf from whereever we expect to find
    338    it.  */
    339 static krb5_error_code
    340 os_init_paths(krb5_context ctx, krb5_boolean kdc)
    341 {
    342     krb5_error_code	retval = 0;
    343     profile_filespec_t *files = 0;
    344     krb5_boolean secure = ctx->profile_secure;
    345 
    346 #ifdef KRB5_DNS_LOOKUP
    347     ctx->profile_in_memory = 0;
    348 #endif /* KRB5_DNS_LOOKUP */
    349 
    350     retval = os_get_default_config_files(&files, secure);
    351 
    352     if (retval == 0 && kdc)
    353 	retval = add_kdc_config_file(&files);
    354 
    355     if (!retval) {
    356         retval = profile_init((const_profile_filespec_t *) files,
    357 			      &ctx->profile);
    358 
    359 #ifdef KRB5_DNS_LOOKUP
    360         /* if none of the filenames can be opened use an empty profile */
    361         if (retval == ENOENT) {
    362             retval = profile_init(NULL, &ctx->profile);
    363             if (!retval)
    364                 ctx->profile_in_memory = 1;
    365         }
    366 #endif /* KRB5_DNS_LOOKUP */
    367     }
    368 
    369     if (files)
    370         free_filespecs(files);
    371 
    372     if (retval)
    373         ctx->profile = 0;
    374 
    375     if (retval == ENOENT)
    376         return KRB5_CONFIG_CANTOPEN;
    377 
    378     if ((retval == PROF_SECTION_NOTOP) ||
    379         (retval == PROF_SECTION_SYNTAX) ||
    380         (retval == PROF_RELATION_SYNTAX) ||
    381         (retval == PROF_EXTRA_CBRACE) ||
    382         (retval == PROF_MISSING_OBRACE))
    383         return KRB5_CONFIG_BADFORMAT;
    384 
    385     return retval;
    386 }
    387 #endif /* !_KERNEL */
    388 
    389 /*ARGSUSED1*/
    390 krb5_error_code
    391 krb5_os_init_context(krb5_context ctx, krb5_boolean kdc)
    392 {
    393 	krb5_os_context os_ctx;
    394 	krb5_error_code	retval = 0;
    395 #ifdef _WIN32
    396     WORD wVersionRequested;
    397     WSADATA wsaData;
    398 #endif /* _WIN32 */
    399 
    400 	os_ctx = ctx->os_context;
    401 	os_ctx->magic = KV5M_OS_CONTEXT;
    402 	os_ctx->time_offset = 0;
    403 	os_ctx->usec_offset = 0;
    404 	os_ctx->os_flags = 0;
    405 	os_ctx->default_ccname = 0;
    406 
    407 #ifndef _KERNEL
    408 	ctx->vtbl = 0;
    409 	PLUGIN_DIR_INIT(&ctx->libkrb5_plugins);
    410 	PLUGIN_DIR_INIT(&ctx->preauth_plugins);
    411 	ctx->preauth_context = NULL;
    412 
    413 	retval = os_init_paths(ctx, kdc);
    414 	/*
    415 	 * If there's an error in the profile, return an error.  Just
    416 	 * ignoring the error is a Bad Thing (tm).
    417 	 */
    418 
    419         if (!retval) {
    420                 krb5_cc_set_default_name(ctx, NULL);
    421 
    422 #ifdef _WIN32
    423                 /* We initialize winsock to version 1.1 but
    424                  * we do not care if we succeed or fail.
    425                  */
    426                 wVersionRequested = 0x0101;
    427                 WSAStartup (wVersionRequested, &wsaData);
    428 #endif /* _WIN32 */
    429         }
    430 
    431 #endif
    432 	return retval;
    433 }
    434 
    435 #ifndef _KERNEL
    436 
    437 krb5_error_code KRB5_CALLCONV
    438 krb5_get_profile (krb5_context ctx, profile_t *profile)
    439 {
    440     return profile_copy (ctx->profile, profile);
    441 }
    442 
    443 #endif
    444 
    445 #ifndef _KERNEL
    446 
    447 krb5_error_code
    448 krb5_set_config_files(krb5_context ctx, const char **filenames)
    449 {
    450 	krb5_error_code retval;
    451 	profile_t	profile;
    452 
    453 	retval = profile_init(filenames, &profile);
    454 	if (retval)
    455 		return retval;
    456 
    457 	if (ctx->profile)
    458 		profile_release(ctx->profile);
    459 	ctx->profile = profile;
    460 
    461 	return 0;
    462 }
    463 
    464 krb5_error_code KRB5_CALLCONV
    465 krb5_get_default_config_files(char ***pfilenames)
    466 {
    467     if (!pfilenames)
    468         return EINVAL;
    469     return os_get_default_config_files(pfilenames, FALSE);
    470 }
    471 
    472 void KRB5_CALLCONV
    473 krb5_free_config_files(char **filenames)
    474 {
    475     free_filespecs(filenames);
    476 }
    477 
    478 #endif /* _KERNEL */
    479 
    480 #ifndef _KERNEL
    481 
    482 krb5_error_code
    483 krb5_secure_config_files(krb5_context ctx)
    484 {
    485 	/* Obsolete interface; always return an error.
    486 
    487 	   This function should be removed next time a major version
    488 	   number change happens.  */
    489 	krb5_error_code retval;
    490 
    491 	if (ctx->profile) {
    492 		profile_release(ctx->profile);
    493 		ctx->profile = 0;
    494 	}
    495 
    496 	ctx->profile_secure = TRUE;
    497 	retval = os_init_paths(ctx, FALSE);
    498 	if (retval)
    499 		return retval;
    500 
    501 	return KRB5_OBSOLETE_FN;
    502 }
    503 
    504 #endif /* _KERNEL */
    505 
    506 void
    507 krb5_os_free_context(krb5_context ctx)
    508 {
    509 	krb5_os_context os_ctx;
    510 
    511 	os_ctx = ctx->os_context;
    512 
    513 	if (os_ctx->default_ccname) {
    514 		FREE(os_ctx->default_ccname,
    515 			strlen(os_ctx->default_ccname) + 1);
    516                 os_ctx->default_ccname = 0;
    517         }
    518 
    519 	os_ctx->magic = 0;
    520 
    521 #ifndef _KERNEL
    522 	if (ctx->profile) {
    523 		profile_release(ctx->profile);
    524 	    ctx->profile = 0;
    525 	}
    526 
    527 	if (ctx->preauth_context) {
    528 		krb5_free_preauth_context(ctx);
    529 		ctx->preauth_context = NULL;
    530 	}
    531 	krb5int_close_plugin_dirs (&ctx->preauth_plugins);
    532 	krb5int_close_plugin_dirs (&ctx->libkrb5_plugins);
    533 
    534 #endif
    535 }
    536 #ifdef _WIN32
    537         WSACleanup();
    538 #endif /* _WIN32 */
    539