Move some infra bits of lib/krb5/ to lib/base/ (1)
This is the first of two commits in a series that must be picked together. This series of two commits moves parts of lib/krb5/ infrastructure functionality to lib/base/, leaving behind wrappers. This commit only renames files to enable git log/diff/blame to follow the renames: to help future code archeology, and to make reviewing these two commits easier. The next commit in this series ensures that the moved files have the correct content (i.e., defining heim APIs instead of krb5 APIs), and will create files in lib/krb5 with the same names and krb5 API wrappers around the new heim API functions. The next commit also explains the motivation, which, briefly, is to: - remove krb5 API usage from lib/gssapi/, - enable the use of configuration and plugins in lib/hx509/ (as well as lib/gssapi/ and future projects), and - enable the further disentanglement of bx509d from kdc/.
This commit is contained in:
1483
lib/base/config_file.c
Normal file
1483
lib/base/config_file.c
Normal file
File diff suppressed because it is too large
Load Diff
649
lib/base/config_reg.c
Normal file
649
lib/base/config_reg.c
Normal file
@@ -0,0 +1,649 @@
|
||||
/***********************************************************************
|
||||
* Copyright (c) 2010, Secure Endpoints Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
#include "krb5_locl.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#error config_reg.c is only for Windows
|
||||
#endif
|
||||
|
||||
#include <shlwapi.h>
|
||||
|
||||
#ifndef MAX_DWORD
|
||||
#define MAX_DWORD 0xFFFFFFFF
|
||||
#endif
|
||||
|
||||
#define REGPATH_KERBEROS "SOFTWARE\\Kerberos"
|
||||
#define REGPATH_HEIMDAL "SOFTWARE\\Heimdal"
|
||||
|
||||
/**
|
||||
* Store a string as a registry value of the specified type
|
||||
*
|
||||
* The following registry types are handled:
|
||||
*
|
||||
* - REG_DWORD: The string is converted to a number.
|
||||
*
|
||||
* - REG_SZ: The string is stored as is.
|
||||
*
|
||||
* - REG_EXPAND_SZ: The string is stored as is.
|
||||
*
|
||||
* - REG_MULTI_SZ:
|
||||
*
|
||||
* . If a separator is specified, the input string is broken
|
||||
* up into multiple strings and stored as a multi-sz.
|
||||
*
|
||||
* . If no separator is provided, the input string is stored
|
||||
* as a multi-sz.
|
||||
*
|
||||
* - REG_NONE:
|
||||
*
|
||||
* . If the string is all numeric, it will be stored as a
|
||||
* REG_DWORD.
|
||||
*
|
||||
* . Otherwise, the string is stored as a REG_SZ.
|
||||
*
|
||||
* Other types are rejected.
|
||||
*
|
||||
* If cb_data is MAX_DWORD, the string pointed to by data must be nul-terminated
|
||||
* otherwise a buffer overrun will occur.
|
||||
*
|
||||
* @param [in]valuename Name of the registry value to be modified or created
|
||||
* @param [in]type Type of the value. REG_NONE if unknown
|
||||
* @param [in]data The input string to be stored in the registry.
|
||||
* @param [in]cb_data Size of the input string in bytes. MAX_DWORD if unknown.
|
||||
* @param [in]separator Separator character for parsing strings.
|
||||
*
|
||||
* @retval 0 if success or non-zero on error.
|
||||
* If non-zero is returned, an error message has been set using
|
||||
* krb5_set_error_message().
|
||||
*
|
||||
*/
|
||||
KRB5_LIB_FUNCTION int KRB5_LIB_CALL
|
||||
_krb5_store_string_to_reg_value(krb5_context context,
|
||||
HKEY key, const char * valuename,
|
||||
DWORD type, const char *data, DWORD cb_data,
|
||||
const char * separator)
|
||||
{
|
||||
LONG rcode;
|
||||
DWORD dwData;
|
||||
BYTE static_buffer[16384];
|
||||
BYTE *pbuffer = &static_buffer[0];
|
||||
|
||||
if (data == NULL)
|
||||
{
|
||||
if (context)
|
||||
krb5_set_error_message(context, 0,
|
||||
"'data' must not be NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cb_data == MAX_DWORD)
|
||||
{
|
||||
cb_data = (DWORD)strlen(data) + 1;
|
||||
}
|
||||
else if ((type == REG_MULTI_SZ && cb_data >= sizeof(static_buffer) - 1) ||
|
||||
cb_data >= sizeof(static_buffer))
|
||||
{
|
||||
if (context)
|
||||
krb5_set_error_message(context, 0, "cb_data too big");
|
||||
return -1;
|
||||
}
|
||||
else if (data[cb_data-1] != '\0')
|
||||
{
|
||||
memcpy(static_buffer, data, cb_data);
|
||||
static_buffer[cb_data++] = '\0';
|
||||
if (type == REG_MULTI_SZ)
|
||||
static_buffer[cb_data++] = '\0';
|
||||
data = static_buffer;
|
||||
}
|
||||
|
||||
if (type == REG_NONE)
|
||||
{
|
||||
/*
|
||||
* If input is all numeric, convert to DWORD and save as REG_DWORD.
|
||||
* Otherwise, store as REG_SZ.
|
||||
*/
|
||||
if ( StrToIntExA( data, STIF_SUPPORT_HEX, &dwData) )
|
||||
{
|
||||
type = REG_DWORD;
|
||||
} else {
|
||||
type = REG_SZ;
|
||||
}
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case REG_SZ:
|
||||
case REG_EXPAND_SZ:
|
||||
rcode = RegSetValueEx(key, valuename, 0, type, data, cb_data);
|
||||
if (rcode)
|
||||
{
|
||||
if (context)
|
||||
krb5_set_error_message(context, 0,
|
||||
"Unexpected error when setting registry value %s gle 0x%x",
|
||||
valuename,
|
||||
GetLastError());
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case REG_MULTI_SZ:
|
||||
if (separator && *separator)
|
||||
{
|
||||
char *cp;
|
||||
|
||||
if (data != static_buffer)
|
||||
static_buffer[cb_data++] = '\0';
|
||||
|
||||
for ( cp = static_buffer; cp < static_buffer+cb_data; cp++)
|
||||
{
|
||||
if (*cp == *separator)
|
||||
*cp = '\0';
|
||||
}
|
||||
|
||||
rcode = RegSetValueEx(key, valuename, 0, type, data, cb_data);
|
||||
if (rcode)
|
||||
{
|
||||
if (context)
|
||||
krb5_set_error_message(context, 0,
|
||||
"Unexpected error when setting registry value %s gle 0x%x",
|
||||
valuename,
|
||||
GetLastError());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case REG_DWORD:
|
||||
if ( !StrToIntExA( data, STIF_SUPPORT_HEX, &dwData) )
|
||||
{
|
||||
if (context)
|
||||
krb5_set_error_message(context, 0,
|
||||
"Unexpected error when parsing %s as number gle 0x%x",
|
||||
data,
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
rcode = RegSetValueEx(key, valuename, 0, type, (BYTE *)&dwData, sizeof(DWORD));
|
||||
if (rcode)
|
||||
{
|
||||
if (context)
|
||||
krb5_set_error_message(context, 0,
|
||||
"Unexpected error when setting registry value %s gle 0x%x",
|
||||
valuename,
|
||||
GetLastError());
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a registry value as a string
|
||||
*
|
||||
* @see _krb5_parse_reg_value_as_multi_string()
|
||||
*/
|
||||
KRB5_LIB_FUNCTION char * KRB5_LIB_CALL
|
||||
_krb5_parse_reg_value_as_string(krb5_context context,
|
||||
HKEY key, const char * valuename,
|
||||
DWORD type, DWORD cb_data)
|
||||
{
|
||||
return _krb5_parse_reg_value_as_multi_string(context, key, valuename,
|
||||
type, cb_data, " ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a registry value as a multi string
|
||||
*
|
||||
* The following registry value types are handled:
|
||||
*
|
||||
* - REG_DWORD: The decimal string representation is used as the
|
||||
* value.
|
||||
*
|
||||
* - REG_SZ: The string is used as-is.
|
||||
*
|
||||
* - REG_EXPAND_SZ: Environment variables in the string are expanded
|
||||
* and the result is used as the value.
|
||||
*
|
||||
* - REG_MULTI_SZ: The list of strings is concatenated using the
|
||||
* separator. No quoting is performed.
|
||||
*
|
||||
* Any other value type is rejected.
|
||||
*
|
||||
* @param [in]valuename Name of the registry value to be queried
|
||||
* @param [in]type Type of the value. REG_NONE if unknown
|
||||
* @param [in]cbdata Size of value. 0 if unknown.
|
||||
* @param [in]separator Separator character for concatenating strings.
|
||||
*
|
||||
* @a type and @a cbdata are only considered valid if both are
|
||||
* specified.
|
||||
*
|
||||
* @retval The registry value string, or NULL if there was an error.
|
||||
* If NULL is returned, an error message has been set using
|
||||
* krb5_set_error_message().
|
||||
*/
|
||||
KRB5_LIB_FUNCTION char * KRB5_LIB_CALL
|
||||
_krb5_parse_reg_value_as_multi_string(krb5_context context,
|
||||
HKEY key, const char * valuename,
|
||||
DWORD type, DWORD cb_data, char *separator)
|
||||
{
|
||||
LONG rcode = ERROR_MORE_DATA;
|
||||
|
||||
BYTE static_buffer[16384];
|
||||
BYTE *pbuffer = &static_buffer[0];
|
||||
DWORD cb_alloc = sizeof(static_buffer);
|
||||
char *ret_string = NULL;
|
||||
|
||||
/* If we know a type and cb_data from a previous call to
|
||||
* RegEnumValue(), we use it. Otherwise we use the
|
||||
* static_buffer[] and query directly. We do this to minimize the
|
||||
* number of queries. */
|
||||
|
||||
if (type == REG_NONE || cb_data == 0) {
|
||||
|
||||
pbuffer = &static_buffer[0];
|
||||
cb_alloc = cb_data = sizeof(static_buffer);
|
||||
rcode = RegQueryValueExA(key, valuename, NULL, &type, pbuffer, &cb_data);
|
||||
|
||||
if (rcode == ERROR_SUCCESS &&
|
||||
|
||||
((type != REG_SZ &&
|
||||
type != REG_EXPAND_SZ) || cb_data + 1 <= sizeof(static_buffer)) &&
|
||||
|
||||
(type != REG_MULTI_SZ || cb_data + 2 <= sizeof(static_buffer)))
|
||||
goto have_data;
|
||||
|
||||
if (rcode != ERROR_MORE_DATA && rcode != ERROR_SUCCESS)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Either we don't have the data or we aren't sure of the size
|
||||
* (due to potentially missing terminating NULs). */
|
||||
|
||||
switch (type) {
|
||||
case REG_DWORD:
|
||||
if (cb_data != sizeof(DWORD)) {
|
||||
if (context)
|
||||
krb5_set_error_message(context, 0,
|
||||
"Unexpected size while reading registry value %s",
|
||||
valuename);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case REG_SZ:
|
||||
case REG_EXPAND_SZ:
|
||||
|
||||
if (rcode == ERROR_SUCCESS && cb_data > 0 && pbuffer[cb_data - 1] == '\0')
|
||||
goto have_data;
|
||||
|
||||
cb_data += sizeof(char); /* Accout for potential missing NUL
|
||||
* terminator. */
|
||||
break;
|
||||
|
||||
case REG_MULTI_SZ:
|
||||
|
||||
if (rcode == ERROR_SUCCESS && cb_data > 0 && pbuffer[cb_data - 1] == '\0' &&
|
||||
(cb_data == 1 || pbuffer[cb_data - 2] == '\0'))
|
||||
goto have_data;
|
||||
|
||||
cb_data += sizeof(char) * 2; /* Potential missing double NUL
|
||||
* terminator. */
|
||||
break;
|
||||
|
||||
default:
|
||||
if (context)
|
||||
krb5_set_error_message(context, 0,
|
||||
"Unexpected type while reading registry value %s",
|
||||
valuename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cb_data <= sizeof(static_buffer))
|
||||
pbuffer = &static_buffer[0];
|
||||
else {
|
||||
pbuffer = malloc(cb_data);
|
||||
if (pbuffer == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cb_alloc = cb_data;
|
||||
rcode = RegQueryValueExA(key, valuename, NULL, NULL, pbuffer, &cb_data);
|
||||
|
||||
if (rcode != ERROR_SUCCESS) {
|
||||
|
||||
/* This can potentially be from a race condition. I.e. some
|
||||
* other process or thread went and modified the registry
|
||||
* value between the time we queried its size and queried for
|
||||
* its value. Ideally we would retry the query in a loop. */
|
||||
|
||||
if (context)
|
||||
krb5_set_error_message(context, 0,
|
||||
"Unexpected error while reading registry value %s",
|
||||
valuename);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (cb_data > cb_alloc || cb_data == 0) {
|
||||
if (context)
|
||||
krb5_set_error_message(context, 0,
|
||||
"Unexpected size while reading registry value %s",
|
||||
valuename);
|
||||
goto done;
|
||||
}
|
||||
|
||||
have_data:
|
||||
switch (type) {
|
||||
case REG_DWORD:
|
||||
asprintf(&ret_string, "%d", *((DWORD *) pbuffer));
|
||||
break;
|
||||
|
||||
case REG_SZ:
|
||||
{
|
||||
char * str = (char *) pbuffer;
|
||||
|
||||
if (str[cb_data - 1] != '\0') {
|
||||
if (cb_data < cb_alloc)
|
||||
str[cb_data] = '\0';
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (pbuffer != static_buffer) {
|
||||
ret_string = (char *) pbuffer;
|
||||
pbuffer = NULL;
|
||||
} else {
|
||||
ret_string = strdup((char *) pbuffer);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case REG_EXPAND_SZ:
|
||||
{
|
||||
char *str = (char *) pbuffer;
|
||||
char expsz[32768]; /* Size of output buffer for
|
||||
* ExpandEnvironmentStrings() is
|
||||
* limited to 32K. */
|
||||
|
||||
if (str[cb_data - 1] != '\0') {
|
||||
if (cb_data < cb_alloc)
|
||||
str[cb_data] = '\0';
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (ExpandEnvironmentStrings(str, expsz, sizeof(expsz)/sizeof(char)) != 0) {
|
||||
ret_string = strdup(expsz);
|
||||
} else {
|
||||
if (context)
|
||||
krb5_set_error_message(context, 0,
|
||||
"Overflow while expanding environment strings "
|
||||
"for registry value %s", valuename);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case REG_MULTI_SZ:
|
||||
{
|
||||
char * str = (char *) pbuffer;
|
||||
char * iter;
|
||||
|
||||
str[cb_alloc - 1] = '\0';
|
||||
str[cb_alloc - 2] = '\0';
|
||||
|
||||
for (iter = str; *iter;) {
|
||||
size_t len = strlen(iter);
|
||||
|
||||
iter += len;
|
||||
if (iter[1] != '\0')
|
||||
*iter++ = *separator;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (pbuffer != static_buffer) {
|
||||
ret_string = str;
|
||||
pbuffer = NULL;
|
||||
} else {
|
||||
ret_string = strdup(str);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (context)
|
||||
krb5_set_error_message(context, 0,
|
||||
"Unexpected type while reading registry value %s",
|
||||
valuename);
|
||||
}
|
||||
|
||||
done:
|
||||
if (pbuffer != static_buffer && pbuffer != NULL)
|
||||
free(pbuffer);
|
||||
|
||||
return ret_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a registry value as a configuration value
|
||||
*
|
||||
* @see parse_reg_value_as_string()
|
||||
*/
|
||||
static krb5_error_code
|
||||
parse_reg_value(krb5_context context,
|
||||
HKEY key, const char * valuename,
|
||||
DWORD type, DWORD cbdata, krb5_config_section ** parent)
|
||||
{
|
||||
char *reg_string = NULL;
|
||||
krb5_config_section *value;
|
||||
krb5_error_code code = 0;
|
||||
|
||||
reg_string = _krb5_parse_reg_value_as_string(context, key, valuename, type, cbdata);
|
||||
|
||||
if (reg_string == NULL)
|
||||
return KRB5_CONFIG_BADFORMAT;
|
||||
|
||||
value = _krb5_config_get_entry(parent, valuename, krb5_config_string);
|
||||
if (value == NULL) {
|
||||
code = ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (value->u.string != NULL)
|
||||
free(value->u.string);
|
||||
|
||||
value->u.string = reg_string;
|
||||
reg_string = NULL;
|
||||
|
||||
done:
|
||||
if (reg_string != NULL)
|
||||
free(reg_string);
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
parse_reg_values(krb5_context context,
|
||||
HKEY key,
|
||||
krb5_config_section ** parent)
|
||||
{
|
||||
DWORD index;
|
||||
LONG rcode;
|
||||
|
||||
for (index = 0; ; index ++) {
|
||||
char name[16385];
|
||||
DWORD cch = sizeof(name)/sizeof(name[0]);
|
||||
DWORD type;
|
||||
DWORD cbdata = 0;
|
||||
krb5_error_code code;
|
||||
|
||||
rcode = RegEnumValue(key, index, name, &cch, NULL,
|
||||
&type, NULL, &cbdata);
|
||||
if (rcode != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
if (cbdata == 0)
|
||||
continue;
|
||||
|
||||
code = parse_reg_value(context, key, name, type, cbdata, parent);
|
||||
if (code != 0)
|
||||
return code;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
parse_reg_subkeys(krb5_context context,
|
||||
HKEY key,
|
||||
krb5_config_section ** parent)
|
||||
{
|
||||
DWORD index;
|
||||
LONG rcode;
|
||||
|
||||
for (index = 0; ; index ++) {
|
||||
HKEY subkey = NULL;
|
||||
char name[256];
|
||||
DWORD cch = sizeof(name)/sizeof(name[0]);
|
||||
krb5_config_section *section = NULL;
|
||||
krb5_error_code code;
|
||||
|
||||
rcode = RegEnumKeyEx(key, index, name, &cch, NULL, NULL, NULL, NULL);
|
||||
if (rcode != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
rcode = RegOpenKeyEx(key, name, 0, KEY_READ, &subkey);
|
||||
if (rcode != ERROR_SUCCESS)
|
||||
continue;
|
||||
|
||||
section = _krb5_config_get_entry(parent, name, krb5_config_list);
|
||||
if (section == NULL) {
|
||||
RegCloseKey(subkey);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
code = parse_reg_values(context, subkey, §ion->u.list);
|
||||
if (code) {
|
||||
RegCloseKey(subkey);
|
||||
return code;
|
||||
}
|
||||
|
||||
code = parse_reg_subkeys(context, subkey, §ion->u.list);
|
||||
if (code) {
|
||||
RegCloseKey(subkey);
|
||||
return code;
|
||||
}
|
||||
|
||||
RegCloseKey(subkey);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
parse_reg_root(krb5_context context,
|
||||
HKEY key,
|
||||
krb5_config_section ** parent)
|
||||
{
|
||||
krb5_config_section *libdefaults = NULL;
|
||||
krb5_error_code code = 0;
|
||||
|
||||
libdefaults = _krb5_config_get_entry(parent, "libdefaults", krb5_config_list);
|
||||
if (libdefaults == NULL)
|
||||
return krb5_enomem(context);
|
||||
|
||||
code = parse_reg_values(context, key, &libdefaults->u.list);
|
||||
if (code)
|
||||
return code;
|
||||
|
||||
return parse_reg_subkeys(context, key, parent);
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
load_config_from_regpath(krb5_context context,
|
||||
HKEY hk_root,
|
||||
const char* key_path,
|
||||
krb5_config_section ** res)
|
||||
{
|
||||
HKEY key = NULL;
|
||||
LONG rcode;
|
||||
krb5_error_code code = 0;
|
||||
|
||||
rcode = RegOpenKeyEx(hk_root, key_path, 0, KEY_READ, &key);
|
||||
if (rcode == ERROR_SUCCESS) {
|
||||
code = parse_reg_root(context, key, res);
|
||||
RegCloseKey(key);
|
||||
key = NULL;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load configuration from registry
|
||||
*
|
||||
* The registry keys 'HKCU\Software\Heimdal' and
|
||||
* 'HKLM\Software\Heimdal' are treated as krb5.conf files. Each
|
||||
* registry key corresponds to a configuration section (or bound list)
|
||||
* and each value in a registry key is treated as a bound value. The
|
||||
* set of values that are directly under the Heimdal key are treated
|
||||
* as if they were defined in the [libdefaults] section.
|
||||
*
|
||||
* @see parse_reg_value() for details about how each type of value is handled.
|
||||
*/
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
_krb5_load_config_from_registry(krb5_context context,
|
||||
krb5_config_section ** res)
|
||||
{
|
||||
krb5_error_code code;
|
||||
|
||||
code = load_config_from_regpath(context, HKEY_LOCAL_MACHINE,
|
||||
REGPATH_KERBEROS, res);
|
||||
if (code)
|
||||
return code;
|
||||
|
||||
code = load_config_from_regpath(context, HKEY_LOCAL_MACHINE,
|
||||
REGPATH_HEIMDAL, res);
|
||||
if (code)
|
||||
return code;
|
||||
|
||||
code = load_config_from_regpath(context, HKEY_CURRENT_USER,
|
||||
REGPATH_KERBEROS, res);
|
||||
if (code)
|
||||
return code;
|
||||
|
||||
code = load_config_from_regpath(context, HKEY_CURRENT_USER,
|
||||
REGPATH_HEIMDAL, res);
|
||||
if (code)
|
||||
return code;
|
||||
return 0;
|
||||
}
|
||||
700
lib/base/expand_path.c
Normal file
700
lib/base/expand_path.c
Normal file
@@ -0,0 +1,700 @@
|
||||
|
||||
/***********************************************************************
|
||||
* Copyright (c) 2009, Secure Endpoints Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
#include "krb5_locl.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
typedef int PTYPE;
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <shlobj.h>
|
||||
#include <sddl.h>
|
||||
|
||||
/*
|
||||
* Expand a %{TEMP} token
|
||||
*
|
||||
* The %{TEMP} token expands to the temporary path for the current
|
||||
* user as returned by GetTempPath().
|
||||
*
|
||||
* @note: Since the GetTempPath() function relies on the TMP or TEMP
|
||||
* environment variables, this function will failover to the system
|
||||
* temporary directory until the user profile is loaded. In addition,
|
||||
* the returned path may or may not exist.
|
||||
*/
|
||||
static krb5_error_code
|
||||
_expand_temp_folder(krb5_context context, PTYPE param, const char *postfix,
|
||||
const char *arg, char **ret)
|
||||
{
|
||||
TCHAR tpath[MAX_PATH];
|
||||
size_t len;
|
||||
|
||||
if (!GetTempPath(sizeof(tpath)/sizeof(tpath[0]), tpath)) {
|
||||
if (context)
|
||||
krb5_set_error_message(context, EINVAL,
|
||||
"Failed to get temporary path (GLE=%d)",
|
||||
GetLastError());
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
len = strlen(tpath);
|
||||
|
||||
if (len > 0 && tpath[len - 1] == '\\')
|
||||
tpath[len - 1] = '\0';
|
||||
|
||||
*ret = strdup(tpath);
|
||||
|
||||
if (*ret == NULL)
|
||||
return krb5_enomem(context);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern HINSTANCE _krb5_hInstance;
|
||||
|
||||
/*
|
||||
* Expand a %{BINDIR} token
|
||||
*
|
||||
* This is also used to expand a few other tokens on Windows, since
|
||||
* most of the executable binaries end up in the same directory. The
|
||||
* "bin" directory is considered to be the directory in which the
|
||||
* krb5.dll is located.
|
||||
*/
|
||||
static krb5_error_code
|
||||
_expand_bin_dir(krb5_context context, PTYPE param, const char *postfix,
|
||||
const char *arg, char **ret)
|
||||
{
|
||||
TCHAR path[MAX_PATH];
|
||||
TCHAR *lastSlash;
|
||||
DWORD nc;
|
||||
|
||||
nc = GetModuleFileName(_krb5_hInstance, path, sizeof(path)/sizeof(path[0]));
|
||||
if (nc == 0 ||
|
||||
nc == sizeof(path)/sizeof(path[0])) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
lastSlash = strrchr(path, '\\');
|
||||
if (lastSlash != NULL) {
|
||||
TCHAR *fslash = strrchr(lastSlash, '/');
|
||||
|
||||
if (fslash != NULL)
|
||||
lastSlash = fslash;
|
||||
|
||||
*lastSlash = '\0';
|
||||
}
|
||||
|
||||
if (postfix) {
|
||||
if (strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
*ret = strdup(path);
|
||||
if (*ret == NULL)
|
||||
return krb5_enomem(context);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expand a %{USERID} token
|
||||
*
|
||||
* The %{USERID} token expands to the string representation of the
|
||||
* user's SID. The user account that will be used is the account
|
||||
* corresponding to the current thread's security token. This means
|
||||
* that:
|
||||
*
|
||||
* - If the current thread token has the anonymous impersonation
|
||||
* level, the call will fail.
|
||||
*
|
||||
* - If the current thread is impersonating a token at
|
||||
* SecurityIdentification level the call will fail.
|
||||
*
|
||||
*/
|
||||
static krb5_error_code
|
||||
_expand_userid(krb5_context context, PTYPE param, const char *postfix,
|
||||
const char *arg, char **ret)
|
||||
{
|
||||
int rv = EINVAL;
|
||||
HANDLE hThread = NULL;
|
||||
HANDLE hToken = NULL;
|
||||
PTOKEN_OWNER pOwner = NULL;
|
||||
DWORD len = 0;
|
||||
LPTSTR strSid = NULL;
|
||||
|
||||
hThread = GetCurrentThread();
|
||||
|
||||
if (!OpenThreadToken(hThread, TOKEN_QUERY,
|
||||
FALSE, /* Open the thread token as the
|
||||
current thread user. */
|
||||
&hToken)) {
|
||||
|
||||
DWORD le = GetLastError();
|
||||
|
||||
if (le == ERROR_NO_TOKEN) {
|
||||
HANDLE hProcess = GetCurrentProcess();
|
||||
|
||||
le = 0;
|
||||
if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
|
||||
le = GetLastError();
|
||||
}
|
||||
|
||||
if (le != 0) {
|
||||
if (context)
|
||||
krb5_set_error_message(context, rv,
|
||||
"Can't open thread token (GLE=%d)", le);
|
||||
goto _exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (!GetTokenInformation(hToken, TokenOwner, NULL, 0, &len)) {
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||
if (context)
|
||||
krb5_set_error_message(context, rv,
|
||||
"Unexpected error reading token information (GLE=%d)",
|
||||
GetLastError());
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
if (context)
|
||||
krb5_set_error_message(context, rv,
|
||||
"GetTokenInformation() returned truncated buffer");
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
pOwner = malloc(len);
|
||||
if (pOwner == NULL) {
|
||||
if (context)
|
||||
krb5_set_error_message(context, rv, "Out of memory");
|
||||
goto _exit;
|
||||
}
|
||||
} else {
|
||||
if (context)
|
||||
krb5_set_error_message(context, rv, "GetTokenInformation() returned truncated buffer");
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
if (!GetTokenInformation(hToken, TokenOwner, pOwner, len, &len)) {
|
||||
if (context)
|
||||
krb5_set_error_message(context, rv, "GetTokenInformation() failed. GLE=%d", GetLastError());
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
if (!ConvertSidToStringSid(pOwner->Owner, &strSid)) {
|
||||
if (context)
|
||||
krb5_set_error_message(context, rv, "Can't convert SID to string. GLE=%d", GetLastError());
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
*ret = strdup(strSid);
|
||||
if (*ret == NULL && context)
|
||||
krb5_set_error_message(context, rv, "Out of memory");
|
||||
|
||||
rv = 0;
|
||||
|
||||
_exit:
|
||||
if (hToken != NULL)
|
||||
CloseHandle(hToken);
|
||||
|
||||
if (pOwner != NULL)
|
||||
free (pOwner);
|
||||
|
||||
if (strSid != NULL)
|
||||
LocalFree(strSid);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expand a folder identified by a CSIDL
|
||||
*/
|
||||
|
||||
static krb5_error_code
|
||||
_expand_csidl(krb5_context context, PTYPE folder, const char *postfix,
|
||||
const char *arg, char **ret)
|
||||
{
|
||||
TCHAR path[MAX_PATH];
|
||||
size_t len;
|
||||
|
||||
if (SHGetFolderPath(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path) != S_OK) {
|
||||
if (context)
|
||||
krb5_set_error_message(context, EINVAL, "Unable to determine folder path");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
len = strlen(path);
|
||||
|
||||
if (len > 0 && path[len - 1] == '\\')
|
||||
path[len - 1] = '\0';
|
||||
|
||||
if (postfix &&
|
||||
strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
|
||||
return krb5_enomem(context);
|
||||
|
||||
*ret = strdup(path);
|
||||
if (*ret == NULL)
|
||||
return krb5_enomem(context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static krb5_error_code
|
||||
_expand_path(krb5_context context, PTYPE param, const char *postfix,
|
||||
const char *arg, char **ret)
|
||||
{
|
||||
*ret = strdup(postfix);
|
||||
if (*ret == NULL)
|
||||
return krb5_enomem(context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
_expand_temp_folder(krb5_context context, PTYPE param, const char *postfix,
|
||||
const char *arg, char **ret)
|
||||
{
|
||||
const char *p = NULL;
|
||||
|
||||
p = secure_getenv("TEMP");
|
||||
|
||||
if (p)
|
||||
*ret = strdup(p);
|
||||
else
|
||||
*ret = strdup("/tmp");
|
||||
if (*ret == NULL)
|
||||
return krb5_enomem(context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
_expand_userid(krb5_context context, PTYPE param, const char *postfix,
|
||||
const char *arg, char **str)
|
||||
{
|
||||
int ret = asprintf(str, "%ld", (unsigned long)getuid());
|
||||
if (ret < 0 || *str == NULL)
|
||||
return krb5_enomem(context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
_expand_euid(krb5_context context, PTYPE param, const char *postfix,
|
||||
const char *arg, char **str)
|
||||
{
|
||||
int ret = asprintf(str, "%ld", (unsigned long)geteuid());
|
||||
if (ret < 0 || *str == NULL)
|
||||
return krb5_enomem(context);
|
||||
return 0;
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
|
||||
static krb5_error_code
|
||||
_expand_username(krb5_context context, PTYPE param, const char *postfix,
|
||||
const char *arg, char **str)
|
||||
{
|
||||
char user[128];
|
||||
const char *username = roken_get_username(user, sizeof(user));
|
||||
|
||||
if (username == NULL) {
|
||||
krb5_set_error_message(context, ENOTTY,
|
||||
N_("unable to figure out current principal",
|
||||
""));
|
||||
return ENOTTY; /* XXX */
|
||||
}
|
||||
|
||||
*str = strdup(username);
|
||||
if (*str == NULL)
|
||||
return krb5_enomem(context);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
_expand_loginname(krb5_context context, PTYPE param, const char *postfix,
|
||||
const char *arg, char **str)
|
||||
{
|
||||
char user[128];
|
||||
const char *username = roken_get_loginname(user, sizeof(user));
|
||||
|
||||
if (username == NULL) {
|
||||
krb5_set_error_message(context, ENOTTY,
|
||||
N_("unable to figure out current principal",
|
||||
""));
|
||||
return ENOTTY; /* XXX */
|
||||
}
|
||||
|
||||
*str = strdup(username);
|
||||
if (*str == NULL)
|
||||
return krb5_enomem(context);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
_expand_strftime(krb5_context context, PTYPE param, const char *postfix,
|
||||
const char *arg, char **ret)
|
||||
{
|
||||
size_t len;
|
||||
time_t t;
|
||||
char buf[1024];
|
||||
|
||||
t = time(NULL);
|
||||
len = strftime(buf, sizeof(buf), arg, localtime(&t));
|
||||
if (len == 0 || len >= sizeof(buf))
|
||||
return ENOMEM;
|
||||
*ret = strdup(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand an extra token
|
||||
*/
|
||||
|
||||
static krb5_error_code
|
||||
_expand_extra_token(krb5_context context, const char *value, char **ret)
|
||||
{
|
||||
*ret = strdup(value);
|
||||
if (*ret == NULL)
|
||||
return krb5_enomem(context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand a %{null} token
|
||||
*
|
||||
* The expansion of a %{null} token is always the empty string.
|
||||
*/
|
||||
|
||||
static krb5_error_code
|
||||
_expand_null(krb5_context context, PTYPE param, const char *postfix,
|
||||
const char *arg, char **ret)
|
||||
{
|
||||
*ret = strdup("");
|
||||
if (*ret == NULL)
|
||||
return krb5_enomem(context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct {
|
||||
const char * tok;
|
||||
int ftype;
|
||||
#define FTYPE_CSIDL 0
|
||||
#define FTYPE_SPECIAL 1
|
||||
|
||||
PTYPE param;
|
||||
const char * postfix;
|
||||
|
||||
int (*exp_func)(krb5_context, PTYPE, const char *, const char *, char **);
|
||||
|
||||
#define SPECIALP(f, P) FTYPE_SPECIAL, 0, P, f
|
||||
#define SPECIAL(f) SPECIALP(f, NULL)
|
||||
|
||||
} tokens[] = {
|
||||
#ifdef _WIN32
|
||||
#define CSIDLP(C,P) FTYPE_CSIDL, C, P, _expand_csidl
|
||||
#define CSIDL(C) CSIDLP(C, NULL)
|
||||
|
||||
{"APPDATA", CSIDL(CSIDL_APPDATA)}, /* Roaming application data (for current user) */
|
||||
{"COMMON_APPDATA", CSIDL(CSIDL_COMMON_APPDATA)}, /* Application data (all users) */
|
||||
{"LOCAL_APPDATA", CSIDL(CSIDL_LOCAL_APPDATA)}, /* Local application data (for current user) */
|
||||
{"SYSTEM", CSIDL(CSIDL_SYSTEM)}, /* Windows System folder (e.g. %WINDIR%\System32) */
|
||||
{"WINDOWS", CSIDL(CSIDL_WINDOWS)}, /* Windows folder */
|
||||
{"USERCONFIG", CSIDLP(CSIDL_APPDATA, "\\" PACKAGE)}, /* Per user Heimdal configuration file path */
|
||||
{"COMMONCONFIG", CSIDLP(CSIDL_COMMON_APPDATA, "\\" PACKAGE)}, /* Common Heimdal configuration file path */
|
||||
{"LIBDIR", SPECIAL(_expand_bin_dir)},
|
||||
{"BINDIR", SPECIAL(_expand_bin_dir)},
|
||||
{"LIBEXEC", SPECIAL(_expand_bin_dir)},
|
||||
{"SBINDIR", SPECIAL(_expand_bin_dir)},
|
||||
#else
|
||||
{"LOCALSTATEDIR", FTYPE_SPECIAL, 0, LOCALSTATEDIR, _expand_path},
|
||||
{"LIBDIR", FTYPE_SPECIAL, 0, LIBDIR, _expand_path},
|
||||
{"BINDIR", FTYPE_SPECIAL, 0, BINDIR, _expand_path},
|
||||
{"LIBEXEC", FTYPE_SPECIAL, 0, LIBEXECDIR, _expand_path},
|
||||
{"SBINDIR", FTYPE_SPECIAL, 0, SBINDIR, _expand_path},
|
||||
{"euid", SPECIAL(_expand_euid)},
|
||||
{"ruid", SPECIAL(_expand_userid)},
|
||||
{"loginname", SPECIAL(_expand_loginname)},
|
||||
#endif
|
||||
{"username", SPECIAL(_expand_username)},
|
||||
{"TEMP", SPECIAL(_expand_temp_folder)},
|
||||
{"USERID", SPECIAL(_expand_userid)},
|
||||
{"uid", SPECIAL(_expand_userid)},
|
||||
{"null", SPECIAL(_expand_null)},
|
||||
{"strftime", SPECIAL(_expand_strftime)}
|
||||
};
|
||||
|
||||
static krb5_error_code
|
||||
_expand_token(krb5_context context,
|
||||
const char *token,
|
||||
const char *token_end,
|
||||
char **extra_tokens,
|
||||
char **ret)
|
||||
{
|
||||
krb5_error_code errcode;
|
||||
size_t i;
|
||||
char **p;
|
||||
const char *colon;
|
||||
|
||||
*ret = NULL;
|
||||
|
||||
if (token[0] != '%' || token[1] != '{' || token_end[0] != '}' ||
|
||||
token_end - token <= 2) {
|
||||
if (context)
|
||||
krb5_set_error_message(context, EINVAL,"Invalid token.");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
for (p = extra_tokens; p && p[0]; p += 2) {
|
||||
if (strncmp(token+2, p[0], (token_end - token) - 2) == 0)
|
||||
return _expand_extra_token(context, p[1], ret);
|
||||
}
|
||||
|
||||
for (colon=token+2; colon < token_end; colon++)
|
||||
if (*colon == ':')
|
||||
break;
|
||||
|
||||
for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++)
|
||||
if (!strncmp(token+2, tokens[i].tok, (colon - token) - 2)) {
|
||||
char *arg = NULL;
|
||||
|
||||
errcode = 0;
|
||||
if (*colon == ':') {
|
||||
asprintf(&arg, "%.*s", (int)(token_end - colon - 1), colon + 1);
|
||||
if (!arg)
|
||||
errcode = ENOMEM;
|
||||
}
|
||||
if (!errcode)
|
||||
errcode = tokens[i].exp_func(context, tokens[i].param,
|
||||
tokens[i].postfix, arg, ret);
|
||||
free(arg);
|
||||
return errcode;
|
||||
}
|
||||
|
||||
if (context)
|
||||
krb5_set_error_message(context, EINVAL, "Invalid token.");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function to expand tokens in paths.
|
||||
*
|
||||
* Inputs:
|
||||
*
|
||||
* @context A krb5_context
|
||||
* @path_in The path to expand tokens from
|
||||
*
|
||||
* Outputs:
|
||||
*
|
||||
* @ppath_out Path with expanded tokens (caller must free() this)
|
||||
*/
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
_krb5_expand_path_tokens(krb5_context context,
|
||||
const char *path_in,
|
||||
int filepath,
|
||||
char **ppath_out)
|
||||
{
|
||||
return _krb5_expand_path_tokensv(context, path_in, filepath, ppath_out, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
free_extra_tokens(char **extra_tokens)
|
||||
{
|
||||
char **p;
|
||||
|
||||
for (p = extra_tokens; p && *p; p++)
|
||||
free(*p);
|
||||
free(extra_tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function to expand tokens in paths.
|
||||
*
|
||||
* Inputs:
|
||||
*
|
||||
* @context A krb5_context
|
||||
* @path_in The path to expand tokens from
|
||||
* @ppath_out The expanded path
|
||||
* @... Variable number of pairs of strings, the first of each
|
||||
* being a token (e.g., "luser") and the second a string to
|
||||
* replace it with. The list is terminated by a NULL.
|
||||
*
|
||||
* Outputs:
|
||||
*
|
||||
* @ppath_out Path with expanded tokens (caller must free() this)
|
||||
*/
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
_krb5_expand_path_tokensv(krb5_context context,
|
||||
const char *path_in,
|
||||
int filepath,
|
||||
char **ppath_out, ...)
|
||||
{
|
||||
char *tok_begin, *tok_end, *append;
|
||||
char **extra_tokens = NULL;
|
||||
const char *path_left;
|
||||
size_t nargs = 0;
|
||||
size_t len = 0;
|
||||
va_list ap;
|
||||
|
||||
if (path_in == NULL || *path_in == '\0') {
|
||||
*ppath_out = strdup("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
*ppath_out = NULL;
|
||||
|
||||
va_start(ap, ppath_out);
|
||||
while (va_arg(ap, const char *)) {
|
||||
nargs++;
|
||||
va_arg(ap, const char *);
|
||||
}
|
||||
va_end(ap);
|
||||
nargs *= 2;
|
||||
|
||||
/* Get extra tokens */
|
||||
if (nargs) {
|
||||
size_t i;
|
||||
|
||||
extra_tokens = calloc(nargs + 1, sizeof (*extra_tokens));
|
||||
if (extra_tokens == NULL)
|
||||
return krb5_enomem(context);
|
||||
va_start(ap, ppath_out);
|
||||
for (i = 0; i < nargs; i++) {
|
||||
const char *s = va_arg(ap, const char *); /* token key */
|
||||
if (s == NULL)
|
||||
break;
|
||||
extra_tokens[i] = strdup(s);
|
||||
if (extra_tokens[i++] == NULL) {
|
||||
va_end(ap);
|
||||
free_extra_tokens(extra_tokens);
|
||||
return krb5_enomem(context);
|
||||
}
|
||||
s = va_arg(ap, const char *); /* token value */
|
||||
if (s == NULL)
|
||||
s = "";
|
||||
extra_tokens[i] = strdup(s);
|
||||
if (extra_tokens[i] == NULL) {
|
||||
va_end(ap);
|
||||
free_extra_tokens(extra_tokens);
|
||||
return krb5_enomem(context);
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
for (path_left = path_in; path_left && *path_left; ) {
|
||||
|
||||
tok_begin = strstr(path_left, "%{");
|
||||
|
||||
if (tok_begin && tok_begin != path_left) {
|
||||
|
||||
append = malloc((tok_begin - path_left) + 1);
|
||||
if (append) {
|
||||
memcpy(append, path_left, tok_begin - path_left);
|
||||
append[tok_begin - path_left] = '\0';
|
||||
}
|
||||
path_left = tok_begin;
|
||||
|
||||
} else if (tok_begin) {
|
||||
|
||||
tok_end = strchr(tok_begin, '}');
|
||||
if (tok_end == NULL) {
|
||||
free_extra_tokens(extra_tokens);
|
||||
if (*ppath_out)
|
||||
free(*ppath_out);
|
||||
*ppath_out = NULL;
|
||||
if (context)
|
||||
krb5_set_error_message(context, EINVAL, "variable missing }");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (_expand_token(context, tok_begin, tok_end, extra_tokens,
|
||||
&append)) {
|
||||
free_extra_tokens(extra_tokens);
|
||||
if (*ppath_out)
|
||||
free(*ppath_out);
|
||||
*ppath_out = NULL;
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
path_left = tok_end + 1;
|
||||
} else {
|
||||
|
||||
append = strdup(path_left);
|
||||
path_left = NULL;
|
||||
|
||||
}
|
||||
|
||||
if (append == NULL) {
|
||||
|
||||
free_extra_tokens(extra_tokens);
|
||||
if (*ppath_out)
|
||||
free(*ppath_out);
|
||||
*ppath_out = NULL;
|
||||
return krb5_enomem(context);
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
size_t append_len = strlen(append);
|
||||
char * new_str = realloc(*ppath_out, len + append_len + 1);
|
||||
|
||||
if (new_str == NULL) {
|
||||
free_extra_tokens(extra_tokens);
|
||||
free(append);
|
||||
if (*ppath_out)
|
||||
free(*ppath_out);
|
||||
*ppath_out = NULL;
|
||||
return krb5_enomem(context);
|
||||
}
|
||||
|
||||
*ppath_out = new_str;
|
||||
memcpy(*ppath_out + len, append, append_len + 1);
|
||||
len = len + append_len;
|
||||
free(append);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
/* Also deal with slashes */
|
||||
if (filepath && *ppath_out) {
|
||||
char * c;
|
||||
|
||||
for (c = *ppath_out; *c; c++)
|
||||
if (*c == '/')
|
||||
*c = '\\';
|
||||
}
|
||||
#endif
|
||||
|
||||
free_extra_tokens(extra_tokens);
|
||||
return 0;
|
||||
}
|
||||
583
lib/base/log.c
Normal file
583
lib/base/log.c
Normal file
@@ -0,0 +1,583 @@
|
||||
/*
|
||||
* Copyright (c) 1997-2006 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2009 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "krb5_locl.h"
|
||||
#include <assert.h>
|
||||
#include <vis.h>
|
||||
|
||||
struct facility {
|
||||
int min;
|
||||
int max;
|
||||
krb5_log_log_func_t log_func;
|
||||
krb5_log_close_func_t close_func;
|
||||
void *data;
|
||||
};
|
||||
|
||||
static struct facility*
|
||||
log_realloc(krb5_log_facility *f)
|
||||
{
|
||||
struct facility *fp;
|
||||
fp = realloc(f->val, (f->len + 1) * sizeof(*f->val));
|
||||
if(fp == NULL)
|
||||
return NULL;
|
||||
f->len++;
|
||||
f->val = fp;
|
||||
fp += f->len - 1;
|
||||
return fp;
|
||||
}
|
||||
|
||||
struct s2i {
|
||||
const char *s;
|
||||
int val;
|
||||
};
|
||||
|
||||
#define L(X) { #X, LOG_ ## X }
|
||||
|
||||
static struct s2i syslogvals[] = {
|
||||
L(EMERG),
|
||||
L(ALERT),
|
||||
L(CRIT),
|
||||
L(ERR),
|
||||
L(WARNING),
|
||||
L(NOTICE),
|
||||
L(INFO),
|
||||
L(DEBUG),
|
||||
|
||||
L(AUTH),
|
||||
#ifdef LOG_AUTHPRIV
|
||||
L(AUTHPRIV),
|
||||
#endif
|
||||
#ifdef LOG_CRON
|
||||
L(CRON),
|
||||
#endif
|
||||
L(DAEMON),
|
||||
#ifdef LOG_FTP
|
||||
L(FTP),
|
||||
#endif
|
||||
L(KERN),
|
||||
L(LPR),
|
||||
L(MAIL),
|
||||
#ifdef LOG_NEWS
|
||||
L(NEWS),
|
||||
#endif
|
||||
L(SYSLOG),
|
||||
L(USER),
|
||||
#ifdef LOG_UUCP
|
||||
L(UUCP),
|
||||
#endif
|
||||
L(LOCAL0),
|
||||
L(LOCAL1),
|
||||
L(LOCAL2),
|
||||
L(LOCAL3),
|
||||
L(LOCAL4),
|
||||
L(LOCAL5),
|
||||
L(LOCAL6),
|
||||
L(LOCAL7),
|
||||
{ NULL, -1 }
|
||||
};
|
||||
|
||||
static int
|
||||
find_value(const char *s, struct s2i *table)
|
||||
{
|
||||
while(table->s && strcasecmp(table->s, s))
|
||||
table++;
|
||||
return table->val;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_initlog(krb5_context context,
|
||||
const char *program,
|
||||
krb5_log_facility **fac)
|
||||
{
|
||||
krb5_log_facility *f = calloc(1, sizeof(*f));
|
||||
if (f == NULL)
|
||||
return krb5_enomem(context);
|
||||
f->program = strdup(program);
|
||||
if(f->program == NULL){
|
||||
free(f);
|
||||
return krb5_enomem(context);
|
||||
}
|
||||
*fac = f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_addlog_func(krb5_context context,
|
||||
krb5_log_facility *fac,
|
||||
int min,
|
||||
int max,
|
||||
krb5_log_log_func_t log_func,
|
||||
krb5_log_close_func_t close_func,
|
||||
void *data)
|
||||
{
|
||||
struct facility *fp = log_realloc(fac);
|
||||
if (fp == NULL)
|
||||
return krb5_enomem(context);
|
||||
fp->min = min;
|
||||
fp->max = max;
|
||||
fp->log_func = log_func;
|
||||
fp->close_func = close_func;
|
||||
fp->data = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct _heimdal_syslog_data{
|
||||
int priority;
|
||||
};
|
||||
|
||||
static void KRB5_CALLCONV
|
||||
log_syslog(krb5_context context, const char *timestr,
|
||||
const char *msg, void *data)
|
||||
{
|
||||
struct _heimdal_syslog_data *s = data;
|
||||
syslog(s->priority, "%s", msg);
|
||||
}
|
||||
|
||||
static void KRB5_CALLCONV
|
||||
close_syslog(void *data)
|
||||
{
|
||||
free(data);
|
||||
closelog();
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
open_syslog(krb5_context context,
|
||||
krb5_log_facility *facility, int min, int max,
|
||||
const char *sev, const char *fac)
|
||||
{
|
||||
struct _heimdal_syslog_data *sd = malloc(sizeof(*sd));
|
||||
int i;
|
||||
|
||||
if (sd == NULL)
|
||||
return krb5_enomem(context);
|
||||
i = find_value(sev, syslogvals);
|
||||
if(i == -1)
|
||||
i = LOG_ERR;
|
||||
sd->priority = i;
|
||||
i = find_value(fac, syslogvals);
|
||||
if(i == -1)
|
||||
i = LOG_AUTH;
|
||||
sd->priority |= i;
|
||||
roken_openlog(facility->program, LOG_PID | LOG_NDELAY, i);
|
||||
return krb5_addlog_func(context, facility, min, max,
|
||||
log_syslog, close_syslog, sd);
|
||||
}
|
||||
|
||||
struct file_data {
|
||||
const char *filename;
|
||||
const char *mode;
|
||||
struct timeval tv;
|
||||
FILE *fd;
|
||||
int disp;
|
||||
#define FILEDISP_KEEPOPEN 0x1
|
||||
#define FILEDISP_REOPEN 0x2
|
||||
#define FILEDISP_IFEXISTS 0x3
|
||||
int freefilename;
|
||||
};
|
||||
|
||||
static void KRB5_CALLCONV
|
||||
log_file(krb5_context context, const char *timestr, const char *msg, void *data)
|
||||
{
|
||||
struct timeval tv;
|
||||
struct file_data *f = data;
|
||||
char *msgclean;
|
||||
size_t i;
|
||||
size_t j;
|
||||
|
||||
if (f->disp != FILEDISP_KEEPOPEN) {
|
||||
char *filename;
|
||||
int flags = -1;
|
||||
int fd;
|
||||
|
||||
if (f->mode[0] == 'w' && f->mode[1] == 0)
|
||||
flags = O_WRONLY|O_TRUNC;
|
||||
if (f->mode[0] == 'a' && f->mode[1] == 0)
|
||||
flags = O_WRONLY|O_APPEND;
|
||||
assert(flags != -1);
|
||||
|
||||
if (f->disp == FILEDISP_IFEXISTS) {
|
||||
/* Cache failure for 1s */
|
||||
gettimeofday(&tv, NULL);
|
||||
if (tv.tv_sec == f->tv.tv_sec)
|
||||
return;
|
||||
} else {
|
||||
flags |= O_CREAT;
|
||||
}
|
||||
|
||||
if (_krb5_expand_path_tokens(context, f->filename, 1, &filename))
|
||||
return;
|
||||
fd = open(filename, flags, 0666);
|
||||
if (fd == -1) {
|
||||
if (f->disp == FILEDISP_IFEXISTS)
|
||||
gettimeofday(&f->tv, NULL);
|
||||
return;
|
||||
}
|
||||
f->fd = fdopen(fd, f->mode);
|
||||
free(filename);
|
||||
}
|
||||
if(f->fd == NULL)
|
||||
return;
|
||||
/*
|
||||
* make sure the log doesn't contain special chars:
|
||||
* we used to use strvisx(3) to encode the log, but this is
|
||||
* inconsistent with our syslog(3) code which does not do this.
|
||||
* It also makes it inelegant to write data which has already
|
||||
* been quoted such as what krb5_unparse_principal() gives us.
|
||||
* So, we change here to eat the special characters, instead.
|
||||
*/
|
||||
msgclean = strdup(msg);
|
||||
if (msgclean == NULL)
|
||||
goto out;
|
||||
for (i=0, j=0; msg[i]; i++)
|
||||
if (msg[i] >= 32 || msg[i] == '\t')
|
||||
msgclean[j++] = msg[i];
|
||||
fprintf(f->fd, "%s %s\n", timestr, msgclean);
|
||||
free(msgclean);
|
||||
out:
|
||||
if(f->disp != FILEDISP_KEEPOPEN) {
|
||||
fclose(f->fd);
|
||||
f->fd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void KRB5_CALLCONV
|
||||
close_file(void *data)
|
||||
{
|
||||
struct file_data *f = data;
|
||||
if(f->disp == FILEDISP_KEEPOPEN && f->filename)
|
||||
fclose(f->fd);
|
||||
if (f->filename && f->freefilename)
|
||||
free((char *)f->filename);
|
||||
free(data);
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
open_file(krb5_context context, krb5_log_facility *fac, int min, int max,
|
||||
const char *filename, const char *mode, FILE *f, int disp,
|
||||
int freefilename)
|
||||
{
|
||||
struct file_data *fd = malloc(sizeof(*fd));
|
||||
if (fd == NULL) {
|
||||
if (freefilename && filename)
|
||||
free((char *)filename);
|
||||
return krb5_enomem(context);
|
||||
}
|
||||
fd->filename = filename;
|
||||
fd->mode = mode;
|
||||
fd->fd = f;
|
||||
fd->disp = disp;
|
||||
fd->freefilename = freefilename;
|
||||
|
||||
return krb5_addlog_func(context, fac, min, max, log_file, close_file, fd);
|
||||
}
|
||||
|
||||
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_addlog_dest(krb5_context context, krb5_log_facility *f, const char *orig)
|
||||
{
|
||||
krb5_error_code ret = 0;
|
||||
int min = 0, max = 3, n;
|
||||
char c;
|
||||
const char *p = orig;
|
||||
#ifdef _WIN32
|
||||
const char *q;
|
||||
#endif
|
||||
|
||||
n = sscanf(p, "%d%c%d/", &min, &c, &max);
|
||||
if(n == 2){
|
||||
if(ISPATHSEP(c)) {
|
||||
if(min < 0){
|
||||
max = -min;
|
||||
min = 0;
|
||||
}else{
|
||||
max = min;
|
||||
}
|
||||
}
|
||||
if (c == '-')
|
||||
max = -1;
|
||||
}
|
||||
if(n){
|
||||
#ifdef _WIN32
|
||||
q = strrchr(p, '\\');
|
||||
if (q != NULL)
|
||||
p = q;
|
||||
else
|
||||
#endif
|
||||
p = strchr(p, '/');
|
||||
if(p == NULL) {
|
||||
krb5_set_error_message(context, HEIM_ERR_LOG_PARSE,
|
||||
N_("failed to parse \"%s\"", ""), orig);
|
||||
return HEIM_ERR_LOG_PARSE;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
if(strcmp(p, "STDERR") == 0){
|
||||
ret = open_file(context, f, min, max, NULL, NULL, stderr, 1, 0);
|
||||
}else if(strcmp(p, "CONSOLE") == 0){
|
||||
ret = open_file(context, f, min, max, "/dev/console", "w", NULL,
|
||||
FILEDISP_REOPEN, 0);
|
||||
}else if (strncmp(p, "EFILE", 5) == 0 && p[5] == ':') {
|
||||
ret = open_file(context, f, min, max, strdup(p+6), "a", NULL,
|
||||
FILEDISP_IFEXISTS, 1);
|
||||
}else if(strncmp(p, "FILE", 4) == 0 && (p[4] == ':' || p[4] == '=')){
|
||||
char *fn;
|
||||
FILE *file = NULL;
|
||||
int disp = FILEDISP_REOPEN;
|
||||
fn = strdup(p + 5);
|
||||
if (fn == NULL)
|
||||
return krb5_enomem(context);
|
||||
if(p[4] == '='){
|
||||
int i = open(fn, O_WRONLY | O_CREAT |
|
||||
O_TRUNC | O_APPEND, 0666);
|
||||
if(i < 0) {
|
||||
ret = errno;
|
||||
krb5_set_error_message(context, ret,
|
||||
N_("open(%s) logfile: %s", ""), fn,
|
||||
strerror(ret));
|
||||
free(fn);
|
||||
return ret;
|
||||
}
|
||||
rk_cloexec(i);
|
||||
file = fdopen(i, "a");
|
||||
if(file == NULL){
|
||||
ret = errno;
|
||||
close(i);
|
||||
krb5_set_error_message(context, ret,
|
||||
N_("fdopen(%s) logfile: %s", ""),
|
||||
fn, strerror(ret));
|
||||
free(fn);
|
||||
return ret;
|
||||
}
|
||||
disp = FILEDISP_KEEPOPEN;
|
||||
}
|
||||
ret = open_file(context, f, min, max, fn, "a", file, disp, 1);
|
||||
}else if(strncmp(p, "DEVICE", 6) == 0 && (p[6] == ':' || p[6] == '=')){
|
||||
ret = open_file(context, f, min, max, strdup(p + 7), "w", NULL,
|
||||
FILEDISP_REOPEN, 1);
|
||||
}else if(strncmp(p, "SYSLOG", 6) == 0 && (p[6] == '\0' || p[6] == ':')){
|
||||
char severity[128] = "";
|
||||
char facility[128] = "";
|
||||
p += 6;
|
||||
if(*p != '\0')
|
||||
p++;
|
||||
if(strsep_copy(&p, ":", severity, sizeof(severity)) != -1)
|
||||
strsep_copy(&p, ":", facility, sizeof(facility));
|
||||
if(*severity == '\0')
|
||||
strlcpy(severity, "ERR", sizeof(severity));
|
||||
if(*facility == '\0')
|
||||
strlcpy(facility, "AUTH", sizeof(facility));
|
||||
ret = open_syslog(context, f, min, max, severity, facility);
|
||||
}else{
|
||||
ret = HEIM_ERR_LOG_PARSE; /* XXX */
|
||||
krb5_set_error_message (context, ret,
|
||||
N_("unknown log type: %s", ""), p);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_openlog(krb5_context context,
|
||||
const char *program,
|
||||
krb5_log_facility **fac)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char **p, **q;
|
||||
|
||||
ret = krb5_initlog(context, program, fac);
|
||||
if(ret)
|
||||
return ret;
|
||||
|
||||
p = krb5_config_get_strings(context, NULL, "logging", program, NULL);
|
||||
if(p == NULL)
|
||||
p = krb5_config_get_strings(context, NULL, "logging", "default", NULL);
|
||||
if(p){
|
||||
for(q = p; *q && ret == 0; q++)
|
||||
ret = krb5_addlog_dest(context, *fac, *q);
|
||||
krb5_config_free_strings(p);
|
||||
}else
|
||||
ret = krb5_addlog_dest(context, *fac, "SYSLOG");
|
||||
return ret;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_closelog(krb5_context context,
|
||||
krb5_log_facility *fac)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < fac->len; i++)
|
||||
(*fac->val[i].close_func)(fac->val[i].data);
|
||||
free(fac->val);
|
||||
free(fac->program);
|
||||
fac->val = NULL;
|
||||
fac->len = 0;
|
||||
fac->program = NULL;
|
||||
free(fac);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef __attribute__
|
||||
#define __attribute__(X)
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_vlog_msg(krb5_context context,
|
||||
krb5_log_facility *fac,
|
||||
char **reply,
|
||||
int level,
|
||||
const char *fmt,
|
||||
va_list ap)
|
||||
__attribute__ ((__format__ (__printf__, 5, 0)))
|
||||
{
|
||||
|
||||
char *msg = NULL;
|
||||
const char *actual = NULL;
|
||||
char buf[64];
|
||||
time_t t = 0;
|
||||
int i;
|
||||
|
||||
for(i = 0; fac && i < fac->len; i++)
|
||||
if(fac->val[i].min <= level &&
|
||||
(fac->val[i].max < 0 || fac->val[i].max >= level)) {
|
||||
if(t == 0) {
|
||||
t = time(NULL);
|
||||
krb5_format_time(context, t, buf, sizeof(buf), TRUE);
|
||||
}
|
||||
if(actual == NULL) {
|
||||
int ret = vasprintf(&msg, fmt, ap);
|
||||
if(ret < 0 || msg == NULL)
|
||||
actual = fmt;
|
||||
else
|
||||
actual = msg;
|
||||
}
|
||||
(*fac->val[i].log_func)(context, buf, actual, fac->val[i].data);
|
||||
}
|
||||
if(reply == NULL)
|
||||
free(msg);
|
||||
else
|
||||
*reply = msg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_vlog(krb5_context context,
|
||||
krb5_log_facility *fac,
|
||||
int level,
|
||||
const char *fmt,
|
||||
va_list ap)
|
||||
__attribute__ ((__format__ (__printf__, 4, 0)))
|
||||
{
|
||||
return krb5_vlog_msg(context, fac, NULL, level, fmt, ap);
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_log_msg(krb5_context context,
|
||||
krb5_log_facility *fac,
|
||||
int level,
|
||||
char **reply,
|
||||
const char *fmt,
|
||||
...)
|
||||
__attribute__ ((__format__ (__printf__, 5, 6)))
|
||||
{
|
||||
va_list ap;
|
||||
krb5_error_code ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = krb5_vlog_msg(context, fac, reply, level, fmt, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_log(krb5_context context,
|
||||
krb5_log_facility *fac,
|
||||
int level,
|
||||
const char *fmt,
|
||||
...)
|
||||
__attribute__ ((__format__ (__printf__, 4, 5)))
|
||||
{
|
||||
va_list ap;
|
||||
krb5_error_code ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = krb5_vlog(context, fac, level, fmt, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void KRB5_LIB_FUNCTION
|
||||
_krb5_debug(krb5_context context,
|
||||
int level,
|
||||
const char *fmt,
|
||||
...)
|
||||
__attribute__ ((__format__ (__printf__, 3, 4)))
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (context == NULL || context->debug_dest == NULL)
|
||||
return;
|
||||
|
||||
va_start(ap, fmt);
|
||||
krb5_vlog(context, context->debug_dest, level, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
|
||||
_krb5_have_debug(krb5_context context, int level)
|
||||
{
|
||||
if (context == NULL || context->debug_dest == NULL)
|
||||
return 0 ;
|
||||
return 1;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_set_debug_dest(krb5_context context, const char *program,
|
||||
const char *log_spec)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
|
||||
if (context->debug_dest == NULL) {
|
||||
ret = krb5_initlog(context, program, &context->debug_dest);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = krb5_addlog_dest(context, context->debug_dest, log_spec);
|
||||
if (ret)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
771
lib/base/plugin.c
Normal file
771
lib/base/plugin.c
Normal file
@@ -0,0 +1,771 @@
|
||||
/*
|
||||
* Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2018 AuriStor, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "krb5_locl.h"
|
||||
#include "common_plugin.h"
|
||||
|
||||
/*
|
||||
* Definitions:
|
||||
*
|
||||
* module - a category of plugin module, identified by subsystem
|
||||
* (typically "krb5")
|
||||
* dso - a library for a module containing a map of plugin
|
||||
* types to plugins (e.g. "service_locator")
|
||||
* plugin - a set of callbacks and state that follows the
|
||||
* common plugin module definition (version, init, fini)
|
||||
*
|
||||
* Obviously it would have been clearer to use the term "module" rather than
|
||||
* "DSO" given there is an internal "DSO", but "module" was already taken...
|
||||
*
|
||||
* modules := { module: dsos }
|
||||
* dsos := { path, dsohandle, plugins-by-name }
|
||||
* plugins-by-name := { plugin-name: [plug] }
|
||||
* plug := { ftable, ctx }
|
||||
*
|
||||
* Some existing plugin consumers outside libkrb5 use the "krb5" module
|
||||
* namespace, but going forward the module should match the consumer library
|
||||
* name (e.g. libhdb should use the "hdb" module rather than "krb5").
|
||||
*/
|
||||
|
||||
/* global module use, use copy_modules() accessor to access */
|
||||
static heim_dict_t __modules;
|
||||
|
||||
static HEIMDAL_MUTEX modules_mutex = HEIMDAL_MUTEX_INITIALIZER;
|
||||
|
||||
static void
|
||||
copy_modules_once(void *context)
|
||||
{
|
||||
heim_dict_t *modules = (heim_dict_t *)context;
|
||||
|
||||
*modules = heim_dict_create(11);
|
||||
heim_assert(*modules, "plugin modules array allocation failure");
|
||||
}
|
||||
|
||||
/* returns global modules list, refcount +1 */
|
||||
static heim_dict_t
|
||||
copy_modules(void)
|
||||
{
|
||||
static heim_base_once_t modules_once = HEIM_BASE_ONCE_INIT;
|
||||
|
||||
heim_base_once_f(&modules_once, &__modules, copy_modules_once);
|
||||
|
||||
return heim_retain(__modules);
|
||||
}
|
||||
|
||||
/* returns named module, refcount +1 */
|
||||
static heim_dict_t
|
||||
copy_module(const char *name)
|
||||
{
|
||||
heim_string_t module_name = heim_string_create(name);
|
||||
heim_dict_t modules = copy_modules();
|
||||
heim_dict_t module;
|
||||
|
||||
module = heim_dict_copy_value(modules, module_name);
|
||||
if (module == NULL) {
|
||||
module = heim_dict_create(11);
|
||||
heim_dict_set_value(modules, module_name, module);
|
||||
}
|
||||
|
||||
heim_release(modules);
|
||||
heim_release(module_name);
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
/* DSO helpers */
|
||||
struct krb5_dso {
|
||||
heim_string_t path;
|
||||
heim_dict_t plugins_by_name;
|
||||
void *dsohandle;
|
||||
};
|
||||
|
||||
static void
|
||||
dso_dealloc(void *ptr)
|
||||
{
|
||||
struct krb5_dso *p = ptr;
|
||||
|
||||
heim_release(p->path);
|
||||
heim_release(p->plugins_by_name);
|
||||
#ifdef HAVE_DLOPEN
|
||||
if (p->dsohandle)
|
||||
dlclose(p->dsohandle);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* returns internal "DSO" for name, refcount +1 */
|
||||
static struct krb5_dso *
|
||||
copy_internal_dso(const char *name)
|
||||
{
|
||||
heim_string_t dso_name = HSTR("__HEIMDAL_INTERNAL_DSO__");
|
||||
heim_dict_t module = copy_module(name);
|
||||
struct krb5_dso *dso;
|
||||
|
||||
if (module == NULL)
|
||||
return NULL;
|
||||
|
||||
dso = heim_dict_copy_value(module, dso_name);
|
||||
if (dso == NULL) {
|
||||
dso = heim_alloc(sizeof(*dso), "krb5-dso", dso_dealloc);
|
||||
|
||||
dso->path = dso_name;
|
||||
dso->plugins_by_name = heim_dict_create(11);
|
||||
|
||||
heim_dict_set_value(module, dso_name, dso);
|
||||
}
|
||||
|
||||
heim_release(module);
|
||||
|
||||
return dso;
|
||||
}
|
||||
|
||||
struct krb5_plugin {
|
||||
krb5_plugin_common_ftable_p ftable;
|
||||
void *ctx;
|
||||
};
|
||||
|
||||
static void
|
||||
plugin_free(void *ptr)
|
||||
{
|
||||
struct krb5_plugin *pl = ptr;
|
||||
|
||||
if (pl->ftable && pl->ftable->fini)
|
||||
pl->ftable->fini(pl->ctx);
|
||||
}
|
||||
|
||||
struct krb5_plugin_register_ctx {
|
||||
void *symbol;
|
||||
int is_dup;
|
||||
};
|
||||
|
||||
static void
|
||||
plugin_register_check_dup(heim_object_t value, void *ctx, int *stop)
|
||||
{
|
||||
struct krb5_plugin_register_ctx *pc = ctx;
|
||||
struct krb5_plugin *pl = value;
|
||||
|
||||
if (pl->ftable == pc->symbol) {
|
||||
pc->is_dup = 1;
|
||||
*stop = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a plugin symbol name of specific type.
|
||||
* @param context a Keberos context
|
||||
* @param type type of plugin symbol
|
||||
* @param name name of plugin symbol
|
||||
* @param symbol a pointer to the named symbol
|
||||
* @return In case of error a non zero error com_err error is returned
|
||||
* and the Kerberos error string is set.
|
||||
*
|
||||
* @ingroup krb5_support
|
||||
*/
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_plugin_register(krb5_context context,
|
||||
enum krb5_plugin_type type,
|
||||
const char *name,
|
||||
void *symbol)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
heim_array_t plugins;
|
||||
heim_string_t hname;
|
||||
struct krb5_dso *dso;
|
||||
struct krb5_plugin_register_ctx ctx;
|
||||
|
||||
ctx.symbol = symbol;
|
||||
ctx.is_dup = 0;
|
||||
|
||||
/*
|
||||
* It's not clear that PLUGIN_TYPE_FUNC was ever used or supported. It likely
|
||||
* would have caused _krb5_plugin_run_f() to crash as the previous implementation
|
||||
* assumed PLUGIN_TYPE_DATA.
|
||||
*/
|
||||
if (type != PLUGIN_TYPE_DATA) {
|
||||
krb5_warnx(context, "krb5_plugin_register: PLUGIN_TYPE_DATA no longer supported");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
HEIMDAL_MUTEX_lock(&modules_mutex);
|
||||
|
||||
dso = copy_internal_dso("krb5");
|
||||
hname = heim_string_create(name);
|
||||
plugins = heim_dict_copy_value(dso->plugins_by_name, hname);
|
||||
if (plugins != NULL)
|
||||
heim_array_iterate_f(plugins, &ctx, plugin_register_check_dup);
|
||||
else {
|
||||
plugins = heim_array_create();
|
||||
heim_dict_set_value(dso->plugins_by_name, hname, plugins);
|
||||
}
|
||||
|
||||
if (!ctx.is_dup) {
|
||||
/* Note: refactored plugin API only supports common plugin layout */
|
||||
struct krb5_plugin *pl;
|
||||
|
||||
pl = heim_alloc(sizeof(*pl), "krb5-plugin", plugin_free);
|
||||
if (pl == NULL) {
|
||||
ret = krb5_enomem(context);
|
||||
} else {
|
||||
pl->ftable = symbol;
|
||||
ret = pl->ftable->init(context, &pl->ctx);
|
||||
if (ret == 0) {
|
||||
heim_array_append_value(plugins, pl);
|
||||
_krb5_debug(context, 5, "Registered %s plugin", name);
|
||||
}
|
||||
heim_release(pl);
|
||||
}
|
||||
} else
|
||||
ret = 0; /* ignore duplicates to match previous behavior */
|
||||
|
||||
HEIMDAL_MUTEX_unlock(&modules_mutex);
|
||||
|
||||
heim_release(dso);
|
||||
heim_release(hname);
|
||||
heim_release(plugins);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DLOPEN
|
||||
|
||||
static char *
|
||||
resolve_origin(const char *di)
|
||||
{
|
||||
#ifdef HAVE_DLADDR
|
||||
Dl_info dl_info;
|
||||
const char *dname;
|
||||
char *path, *p;
|
||||
#endif
|
||||
|
||||
if (strncmp(di, "$ORIGIN/", sizeof("$ORIGIN/") - 1) &&
|
||||
strcmp(di, "$ORIGIN"))
|
||||
return strdup(di);
|
||||
|
||||
#ifndef HAVE_DLADDR
|
||||
return strdup(LIBDIR "/plugin/krb5");
|
||||
#else /* !HAVE_DLADDR */
|
||||
di += sizeof("$ORIGIN") - 1;
|
||||
|
||||
if (dladdr(_krb5_load_plugins, &dl_info) == 0)
|
||||
return strdup(LIBDIR "/plugin/krb5");
|
||||
|
||||
dname = dl_info.dli_fname;
|
||||
#ifdef _WIN32
|
||||
p = strrchr(dname, '\\');
|
||||
if (p == NULL)
|
||||
#endif
|
||||
p = strrchr(dname, '/');
|
||||
if (p) {
|
||||
if (asprintf(&path, "%.*s%s", (int) (p - dname), dname, di) == -1)
|
||||
return NULL;
|
||||
} else {
|
||||
if (asprintf(&path, "%s%s", dname, di) == -1)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return path;
|
||||
#endif /* !HAVE_DLADDR */
|
||||
}
|
||||
|
||||
#endif /* HAVE_DLOPEN */
|
||||
|
||||
/**
|
||||
* Load plugins (new system) for the given module @name (typically
|
||||
* "krb5") from the given directory @paths.
|
||||
*
|
||||
* Inputs:
|
||||
*
|
||||
* @context A krb5_context
|
||||
* @name Name of plugin module (typically "krb5")
|
||||
* @paths Array of directory paths where to look
|
||||
*/
|
||||
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
|
||||
_krb5_load_plugins(krb5_context context, const char *name, const char **paths)
|
||||
{
|
||||
#ifdef HAVE_DLOPEN
|
||||
heim_string_t s = heim_string_create(name);
|
||||
heim_dict_t module, modules;
|
||||
struct dirent *entry;
|
||||
krb5_error_code ret;
|
||||
const char **di;
|
||||
char *dirname = NULL;
|
||||
DIR *d;
|
||||
#ifdef _WIN32
|
||||
char *plugin_prefix;
|
||||
size_t plugin_prefix_len;
|
||||
|
||||
if (asprintf(&plugin_prefix, "plugin_%s_", name) == -1)
|
||||
return;
|
||||
plugin_prefix_len = (plugin_prefix ? strlen(plugin_prefix) : 0);
|
||||
#endif
|
||||
|
||||
HEIMDAL_MUTEX_lock(&modules_mutex);
|
||||
|
||||
modules = copy_modules();
|
||||
|
||||
module = heim_dict_copy_value(modules, s);
|
||||
if (module == NULL) {
|
||||
module = heim_dict_create(11);
|
||||
if (module == NULL) {
|
||||
HEIMDAL_MUTEX_unlock(&modules_mutex);
|
||||
heim_release(s);
|
||||
heim_release(modules);
|
||||
return;
|
||||
}
|
||||
heim_dict_set_value(modules, s, module);
|
||||
}
|
||||
heim_release(s);
|
||||
heim_release(modules);
|
||||
|
||||
for (di = paths; *di != NULL; di++) {
|
||||
free(dirname);
|
||||
dirname = resolve_origin(*di);
|
||||
if (dirname == NULL)
|
||||
continue;
|
||||
d = opendir(dirname);
|
||||
if (d == NULL)
|
||||
continue;
|
||||
rk_cloexec_dir(d);
|
||||
|
||||
while ((entry = readdir(d)) != NULL) {
|
||||
char *n = entry->d_name;
|
||||
char *path = NULL;
|
||||
heim_string_t spath;
|
||||
struct krb5_dso *p;
|
||||
|
||||
/* skip . and .. */
|
||||
if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0')))
|
||||
continue;
|
||||
|
||||
ret = 0;
|
||||
#ifdef _WIN32
|
||||
/*
|
||||
* On Windows, plugins must be loaded from the same directory as
|
||||
* heimdal.dll (typically the assembly directory) and must have
|
||||
* the name form "plugin_<module>_<name>.dll".
|
||||
*/
|
||||
{
|
||||
char *ext;
|
||||
|
||||
if (strnicmp(n, plugin_prefix, plugin_prefix_len))
|
||||
continue;
|
||||
ext = strrchr(n, '.');
|
||||
if (ext == NULL || stricmp(ext, ".dll"))
|
||||
continue;
|
||||
|
||||
ret = asprintf(&path, "%s\\%s", dirname, n);
|
||||
if (ret < 0 || path == NULL)
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
{ /* support loading bundles on MacOS */
|
||||
size_t len = strlen(n);
|
||||
if (len > 7 && strcmp(&n[len - 7], ".bundle") == 0)
|
||||
ret = asprintf(&path, "%s/%s/Contents/MacOS/%.*s", dirname, n, (int)(len - 7), n);
|
||||
}
|
||||
#endif
|
||||
if (ret < 0 || path == NULL)
|
||||
ret = asprintf(&path, "%s/%s", dirname, n);
|
||||
|
||||
if (ret < 0 || path == NULL)
|
||||
continue;
|
||||
|
||||
spath = heim_string_create(n);
|
||||
if (spath == NULL) {
|
||||
free(path);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* check if already cached */
|
||||
p = heim_dict_copy_value(module, spath);
|
||||
if (p == NULL) {
|
||||
p = heim_alloc(sizeof(*p), "krb5-dso", dso_dealloc);
|
||||
if (p)
|
||||
p->dsohandle = dlopen(path, RTLD_LOCAL|RTLD_LAZY|RTLD_GROUP);
|
||||
if (p && p->dsohandle) {
|
||||
p->path = heim_retain(spath);
|
||||
p->plugins_by_name = heim_dict_create(11);
|
||||
heim_dict_set_value(module, spath, p);
|
||||
}
|
||||
}
|
||||
heim_release(p);
|
||||
heim_release(spath);
|
||||
free(path);
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
free(dirname);
|
||||
HEIMDAL_MUTEX_unlock(&modules_mutex);
|
||||
heim_release(module);
|
||||
#ifdef _WIN32
|
||||
if (plugin_prefix)
|
||||
free(plugin_prefix);
|
||||
#endif
|
||||
#endif /* HAVE_DLOPEN */
|
||||
}
|
||||
|
||||
/**
|
||||
* Unload plugins (new system)
|
||||
*/
|
||||
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
|
||||
_krb5_unload_plugins(krb5_context context, const char *name)
|
||||
{
|
||||
heim_string_t sname = heim_string_create(name);
|
||||
heim_dict_t modules;
|
||||
|
||||
HEIMDAL_MUTEX_lock(&modules_mutex);
|
||||
|
||||
modules = copy_modules();
|
||||
heim_dict_delete_key(modules, sname);
|
||||
|
||||
HEIMDAL_MUTEX_unlock(&modules_mutex);
|
||||
|
||||
heim_release(modules);
|
||||
heim_release(sname);
|
||||
}
|
||||
|
||||
struct iter_ctx {
|
||||
krb5_context context;
|
||||
heim_string_t n;
|
||||
struct krb5_plugin_data *caller;
|
||||
int flags;
|
||||
heim_array_t result;
|
||||
krb5_error_code (KRB5_LIB_CALL *func)(krb5_context, const void *, void *, void *);
|
||||
void *userctx;
|
||||
krb5_error_code ret;
|
||||
};
|
||||
|
||||
#ifdef HAVE_DLOPEN
|
||||
/*
|
||||
* Add plugin from a DSO that exports the plugin structure directly. This is
|
||||
* provided for backwards compatibility with prior versions of Heimdal, but it
|
||||
* does not allow a module to export multiple plugins, nor does it allow
|
||||
* instance validation.
|
||||
*/
|
||||
static heim_array_t
|
||||
add_dso_plugin_struct(krb5_context context,
|
||||
const char *dsopath,
|
||||
void *dsohandle,
|
||||
const char *name)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_plugin_common_ftable_p cpm;
|
||||
struct krb5_plugin *pl;
|
||||
heim_array_t plugins;
|
||||
|
||||
if (dsohandle == NULL)
|
||||
return NULL;
|
||||
|
||||
/* suppress error here because we may be looking for a different plugin type */
|
||||
cpm = (krb5_plugin_common_ftable_p)dlsym(dsohandle, name);
|
||||
if (cpm == NULL)
|
||||
return NULL;
|
||||
|
||||
krb5_warnx(context, "plugin %s uses deprecated loading mechanism", dsopath);
|
||||
|
||||
pl = heim_alloc(sizeof(*pl), "krb5-plugin", plugin_free);
|
||||
|
||||
ret = cpm->init(context, &pl->ctx);
|
||||
if (ret) {
|
||||
krb5_warn(context, ret, "plugin %s failed to initialize", dsopath);
|
||||
heim_release(pl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pl->ftable = cpm;
|
||||
|
||||
plugins = heim_array_create();
|
||||
heim_array_append_value(plugins, pl);
|
||||
heim_release(pl);
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
||||
static krb5_boolean
|
||||
validate_plugin_deps(krb5_context context,
|
||||
struct krb5_plugin_data *caller,
|
||||
const char *dsopath,
|
||||
krb5_get_instance_func_t get_instance)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (get_instance == NULL) {
|
||||
krb5_warnx(context, "plugin %s omitted instance callback",
|
||||
dsopath);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (i = 0; caller->deps[i] != NULL; i++) {
|
||||
uintptr_t heim_instance, plugin_instance;
|
||||
|
||||
heim_instance = caller->get_instance(caller->deps[i]);
|
||||
plugin_instance = get_instance(caller->deps[i]);
|
||||
|
||||
if (heim_instance == 0 || plugin_instance == 0)
|
||||
continue;
|
||||
|
||||
if (heim_instance != plugin_instance) {
|
||||
krb5_warnx(context, "plugin %s library %s linked against different "
|
||||
"instance of Heimdal (got %zu, us %zu)",
|
||||
dsopath, caller->deps[i],
|
||||
plugin_instance, heim_instance);
|
||||
return FALSE;
|
||||
}
|
||||
_krb5_debug(context, 10, "Validated plugin library dependency %s for %s",
|
||||
caller->deps[i], dsopath);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* New interface from Heimdal 8 where a DSO can export a load function
|
||||
* that can return both a libkrb5 instance identifier along with an array
|
||||
* of plugins.
|
||||
*/
|
||||
static heim_array_t
|
||||
add_dso_plugins_load_fn(krb5_context context,
|
||||
struct krb5_plugin_data *caller,
|
||||
const char *dsopath,
|
||||
void *dsohandle)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
heim_array_t plugins;
|
||||
krb5_plugin_load_t load_fn;
|
||||
char *sym;
|
||||
size_t i;
|
||||
krb5_get_instance_func_t get_instance;
|
||||
size_t n_ftables;
|
||||
krb5_plugin_common_ftable_cp *ftables;
|
||||
|
||||
if (asprintf(&sym, "%s_plugin_load", caller->name) == -1)
|
||||
return NULL;
|
||||
|
||||
/* suppress error here because we may be looking for a different plugin type */
|
||||
load_fn = (krb5_plugin_load_t)dlsym(dsohandle, sym);
|
||||
free(sym);
|
||||
if (load_fn == NULL)
|
||||
return NULL;
|
||||
|
||||
ret = load_fn(context, &get_instance, &n_ftables, &ftables);
|
||||
if (ret) {
|
||||
krb5_warn(context, ret, "plugin %s failed to load", dsopath);
|
||||
|
||||
/* fallback to loading structure directly */
|
||||
return add_dso_plugin_struct(context, dsopath,
|
||||
dsohandle, caller->name);
|
||||
}
|
||||
|
||||
if (!validate_plugin_deps(context, caller, dsopath, get_instance))
|
||||
return NULL;
|
||||
|
||||
plugins = heim_array_create();
|
||||
|
||||
for (i = 0; i < n_ftables; i++) {
|
||||
krb5_plugin_common_ftable_cp cpm = ftables[i];
|
||||
struct krb5_plugin *pl;
|
||||
|
||||
pl = heim_alloc(sizeof(*pl), "krb5-plugin", plugin_free);
|
||||
|
||||
ret = cpm->init(context, &pl->ctx);
|
||||
if (ret) {
|
||||
krb5_warn(context, ret, "plugin %s[%zu] failed to initialize",
|
||||
dsopath, i);
|
||||
} else {
|
||||
pl->ftable = rk_UNCONST(cpm);
|
||||
heim_array_append_value(plugins, pl);
|
||||
}
|
||||
heim_release(pl);
|
||||
}
|
||||
|
||||
return plugins;
|
||||
}
|
||||
#endif /* HAVE_DLOPEN */
|
||||
|
||||
static void
|
||||
reduce_by_version(heim_object_t value, void *ctx, int *stop)
|
||||
{
|
||||
struct iter_ctx *s = ctx;
|
||||
struct krb5_plugin *pl = value;
|
||||
|
||||
if (pl->ftable && pl->ftable->version >= s->caller->min_version)
|
||||
heim_array_append_value(s->result, pl);
|
||||
}
|
||||
|
||||
static void
|
||||
search_modules(heim_object_t key, heim_object_t value, void *ctx)
|
||||
{
|
||||
struct iter_ctx *s = ctx;
|
||||
struct krb5_dso *p = value;
|
||||
heim_array_t plugins = heim_dict_copy_value(p->plugins_by_name, s->n);
|
||||
|
||||
#ifdef HAVE_DLOPEN
|
||||
if (plugins == NULL && p->dsohandle) {
|
||||
const char *path = heim_string_get_utf8(p->path);
|
||||
|
||||
plugins = add_dso_plugins_load_fn(s->context,
|
||||
s->caller,
|
||||
path,
|
||||
p->dsohandle);
|
||||
if (plugins) {
|
||||
heim_dict_set_value(p->plugins_by_name, s->n, plugins);
|
||||
_krb5_debug(s->context, 5, "Loaded %zu %s %s plugin%s from %s",
|
||||
heim_array_get_length(plugins),
|
||||
s->caller->module, s->caller->name,
|
||||
heim_array_get_length(plugins) > 1 ? "s" : "",
|
||||
path);
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_DLOPEN */
|
||||
|
||||
if (plugins) {
|
||||
heim_array_iterate_f(plugins, s, reduce_by_version);
|
||||
heim_release(plugins);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
eval_results(heim_object_t value, void *ctx, int *stop)
|
||||
{
|
||||
struct krb5_plugin *pl = value;
|
||||
struct iter_ctx *s = ctx;
|
||||
|
||||
if (s->ret != KRB5_PLUGIN_NO_HANDLE)
|
||||
return;
|
||||
|
||||
s->ret = s->func(s->context, pl->ftable, pl->ctx, s->userctx);
|
||||
if (s->ret != KRB5_PLUGIN_NO_HANDLE
|
||||
&& !(s->flags & KRB5_PLUGIN_INVOKE_ALL))
|
||||
*stop = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run plugins for the given @module (e.g., "krb5") and @name (e.g.,
|
||||
* "kuserok"). Specifically, the @func is invoked once per-plugin with
|
||||
* four arguments: the @context, the plugin symbol value (a pointer to a
|
||||
* struct whose first three fields are the same as common_plugin_ftable),
|
||||
* a context value produced by the plugin's init method, and @userctx.
|
||||
*
|
||||
* @func should unpack arguments for a plugin function and invoke it
|
||||
* with arguments taken from @userctx. @func should save plugin
|
||||
* outputs, if any, in @userctx.
|
||||
*
|
||||
* All loaded and registered plugins are invoked via @func until @func
|
||||
* returns something other than KRB5_PLUGIN_NO_HANDLE. Plugins that
|
||||
* have nothing to do for the given arguments should return
|
||||
* KRB5_PLUGIN_NO_HANDLE.
|
||||
*
|
||||
* Inputs:
|
||||
*
|
||||
* @context A krb5_context
|
||||
* @module Name of module (typically "krb5")
|
||||
* @name Name of pluggable interface (e.g., "kuserok")
|
||||
* @min_version Lowest acceptable plugin minor version number
|
||||
* @flags Flags (none defined at this time)
|
||||
* @userctx Callback data for the callback function @func
|
||||
* @func A callback function, invoked once per-plugin
|
||||
*
|
||||
* Outputs: None, other than the return value and such outputs as are
|
||||
* gathered by @func.
|
||||
*/
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
_krb5_plugin_run_f(krb5_context context,
|
||||
struct krb5_plugin_data *caller,
|
||||
int flags,
|
||||
void *userctx,
|
||||
krb5_error_code (KRB5_LIB_CALL *func)(krb5_context, const void *, void *, void *))
|
||||
{
|
||||
heim_string_t m = heim_string_create(caller->module);
|
||||
heim_dict_t modules, dict = NULL;
|
||||
struct iter_ctx s;
|
||||
|
||||
s.context = context;
|
||||
s.caller = caller;
|
||||
s.n = heim_string_create(caller->name);
|
||||
s.flags = flags;
|
||||
s.result = heim_array_create();
|
||||
s.func = func;
|
||||
s.userctx = userctx;
|
||||
s.ret = KRB5_PLUGIN_NO_HANDLE;
|
||||
|
||||
HEIMDAL_MUTEX_lock(&modules_mutex);
|
||||
|
||||
/* Get loaded plugins */
|
||||
modules = copy_modules();
|
||||
dict = heim_dict_copy_value(modules, m);
|
||||
|
||||
/* Add loaded plugins to s.result array */
|
||||
if (dict)
|
||||
heim_dict_iterate_f(dict, &s, search_modules);
|
||||
|
||||
/* We don't need to hold modules_mutex during plugin invocation */
|
||||
HEIMDAL_MUTEX_unlock(&modules_mutex);
|
||||
|
||||
/* Invoke loaded plugins */
|
||||
heim_array_iterate_f(s.result, &s, eval_results);
|
||||
|
||||
heim_release(s.result);
|
||||
heim_release(s.n);
|
||||
heim_release(dict);
|
||||
heim_release(m);
|
||||
heim_release(modules);
|
||||
|
||||
return s.ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a cookie identifying this instance of a library.
|
||||
*
|
||||
* Inputs:
|
||||
*
|
||||
* @context A krb5_context
|
||||
* @module Our library name or a library we depend on
|
||||
*
|
||||
* Outputs: The instance cookie
|
||||
*
|
||||
* @ingroup krb5_support
|
||||
*/
|
||||
|
||||
KRB5_LIB_FUNCTION uintptr_t KRB5_LIB_CALL
|
||||
krb5_get_instance(const char *libname)
|
||||
{
|
||||
static const char *instance = "libkrb5";
|
||||
|
||||
if (strcmp(libname, "krb5") == 0)
|
||||
return (uintptr_t)instance;
|
||||
|
||||
return 0;
|
||||
}
|
||||
359
lib/base/warn.c
Normal file
359
lib/base/warn.c
Normal file
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(disable: 4646)
|
||||
# pragma warning(disable: 4716)
|
||||
#endif
|
||||
|
||||
#include "krb5_locl.h"
|
||||
#include <err.h>
|
||||
|
||||
static krb5_error_code _warnerr(krb5_context context, int do_errtext,
|
||||
krb5_error_code code, int level, const char *fmt, va_list ap)
|
||||
__attribute__ ((__format__ (__printf__, 5, 0)));
|
||||
|
||||
static krb5_error_code
|
||||
_warnerr(krb5_context context, int do_errtext,
|
||||
krb5_error_code code, int level, const char *fmt, va_list ap)
|
||||
{
|
||||
char xfmt[7] = "";
|
||||
const char *args[2], **arg;
|
||||
char *msg = NULL;
|
||||
const char *err_str = NULL;
|
||||
krb5_error_code ret;
|
||||
|
||||
args[0] = args[1] = NULL;
|
||||
arg = args;
|
||||
if(fmt){
|
||||
strlcat(xfmt, "%s", sizeof(xfmt));
|
||||
if(do_errtext)
|
||||
strlcat(xfmt, ": ", sizeof(xfmt));
|
||||
ret = vasprintf(&msg, fmt, ap);
|
||||
if(ret < 0 || msg == NULL)
|
||||
return ENOMEM;
|
||||
*arg++ = msg;
|
||||
}
|
||||
if(context && do_errtext){
|
||||
strlcat(xfmt, "%s", sizeof(xfmt));
|
||||
|
||||
err_str = krb5_get_error_message(context, code);
|
||||
if (err_str != NULL) {
|
||||
*arg = err_str;
|
||||
} else {
|
||||
*arg= "<unknown error>";
|
||||
}
|
||||
}
|
||||
|
||||
if(context && context->warn_dest)
|
||||
krb5_log(context, context->warn_dest, level, xfmt, args[0], args[1]);
|
||||
else
|
||||
warnx(xfmt, args[0], args[1]);
|
||||
free(msg);
|
||||
krb5_free_error_message(context, err_str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define FUNC(ETEXT, CODE, LEVEL) \
|
||||
krb5_error_code ret; \
|
||||
va_list ap; \
|
||||
va_start(ap, fmt); \
|
||||
ret = _warnerr(context, ETEXT, CODE, LEVEL, fmt, ap); \
|
||||
va_end(ap);
|
||||
|
||||
#define FUNC_NORET(ETEXT, CODE, LEVEL) \
|
||||
va_list ap; \
|
||||
va_start(ap, fmt); \
|
||||
(void) _warnerr(context, ETEXT, CODE, LEVEL, fmt, ap); \
|
||||
va_end(ap);
|
||||
|
||||
#undef __attribute__
|
||||
#define __attribute__(X)
|
||||
|
||||
/**
|
||||
* Log a warning to the log, default stderr, include the error from
|
||||
* the last failure.
|
||||
*
|
||||
* @param context A Kerberos 5 context.
|
||||
* @param code error code of the last error
|
||||
* @param fmt message to print
|
||||
* @param ap arguments
|
||||
*
|
||||
* @ingroup krb5_error
|
||||
*/
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_vwarn(krb5_context context, krb5_error_code code,
|
||||
const char *fmt, va_list ap)
|
||||
__attribute__ ((__format__ (__printf__, 3, 0)))
|
||||
{
|
||||
return _warnerr(context, 1, code, 1, fmt, ap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a warning to the log, default stderr, include the error from
|
||||
* the last failure.
|
||||
*
|
||||
* @param context A Kerberos 5 context.
|
||||
* @param code error code of the last error
|
||||
* @param fmt message to print
|
||||
*
|
||||
* @ingroup krb5_error
|
||||
*/
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_warn(krb5_context context, krb5_error_code code, const char *fmt, ...)
|
||||
__attribute__ ((__format__ (__printf__, 3, 4)))
|
||||
{
|
||||
FUNC(1, code, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a warning to the log, default stderr.
|
||||
*
|
||||
* @param context A Kerberos 5 context.
|
||||
* @param fmt message to print
|
||||
* @param ap arguments
|
||||
*
|
||||
* @ingroup krb5_error
|
||||
*/
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_vwarnx(krb5_context context, const char *fmt, va_list ap)
|
||||
__attribute__ ((__format__ (__printf__, 2, 0)))
|
||||
{
|
||||
return _warnerr(context, 0, 0, 1, fmt, ap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a warning to the log, default stderr.
|
||||
*
|
||||
* @param context A Kerberos 5 context.
|
||||
* @param fmt message to print
|
||||
*
|
||||
* @ingroup krb5_error
|
||||
*/
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_warnx(krb5_context context, const char *fmt, ...)
|
||||
__attribute__ ((__format__ (__printf__, 2, 3)))
|
||||
{
|
||||
FUNC(0, 0, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a warning to the log, default stderr, include bthe error from
|
||||
* the last failure and then exit.
|
||||
*
|
||||
* @param context A Kerberos 5 context
|
||||
* @param eval the exit code to exit with
|
||||
* @param code error code of the last error
|
||||
* @param fmt message to print
|
||||
* @param ap arguments
|
||||
*
|
||||
* @ingroup krb5_error
|
||||
*/
|
||||
|
||||
KRB5_LIB_NORETURN_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_verr(krb5_context context, int eval, krb5_error_code code,
|
||||
const char *fmt, va_list ap)
|
||||
__attribute__ ((__noreturn__, __format__ (__printf__, 4, 0)))
|
||||
{
|
||||
_warnerr(context, 1, code, 0, fmt, ap);
|
||||
exit(eval);
|
||||
UNREACHABLE(return 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a warning to the log, default stderr, include bthe error from
|
||||
* the last failure and then exit.
|
||||
*
|
||||
* @param context A Kerberos 5 context
|
||||
* @param eval the exit code to exit with
|
||||
* @param code error code of the last error
|
||||
* @param fmt message to print
|
||||
*
|
||||
* @ingroup krb5_error
|
||||
*/
|
||||
|
||||
KRB5_LIB_NORETURN_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_err(krb5_context context, int eval, krb5_error_code code,
|
||||
const char *fmt, ...)
|
||||
__attribute__ ((__noreturn__, __format__ (__printf__, 4, 5)))
|
||||
{
|
||||
FUNC_NORET(1, code, 0);
|
||||
exit(eval);
|
||||
UNREACHABLE(return 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a warning to the log, default stderr, and then exit.
|
||||
*
|
||||
* @param context A Kerberos 5 context
|
||||
* @param eval the exit code to exit with
|
||||
* @param fmt message to print
|
||||
* @param ap arguments
|
||||
*
|
||||
* @ingroup krb5_error
|
||||
*/
|
||||
|
||||
KRB5_LIB_NORETURN_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_verrx(krb5_context context, int eval, const char *fmt, va_list ap)
|
||||
__attribute__ ((__noreturn__, __format__ (__printf__, 3, 0)))
|
||||
{
|
||||
_warnerr(context, 0, 0, 0, fmt, ap);
|
||||
exit(eval);
|
||||
UNREACHABLE(return 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a warning to the log, default stderr, and then exit.
|
||||
*
|
||||
* @param context A Kerberos 5 context
|
||||
* @param eval the exit code to exit with
|
||||
* @param fmt message to print
|
||||
*
|
||||
* @ingroup krb5_error
|
||||
*/
|
||||
|
||||
KRB5_LIB_NORETURN_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_errx(krb5_context context, int eval, const char *fmt, ...)
|
||||
__attribute__ ((__noreturn__, __format__ (__printf__, 3, 4)))
|
||||
{
|
||||
FUNC_NORET(0, 0, 0);
|
||||
exit(eval);
|
||||
UNREACHABLE(return 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a warning to the log, default stderr, include bthe error from
|
||||
* the last failure and then abort.
|
||||
*
|
||||
* @param context A Kerberos 5 context
|
||||
* @param code error code of the last error
|
||||
* @param fmt message to print
|
||||
* @param ap arguments
|
||||
*
|
||||
* @ingroup krb5_error
|
||||
*/
|
||||
|
||||
KRB5_LIB_NORETURN_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_vabort(krb5_context context, krb5_error_code code,
|
||||
const char *fmt, va_list ap)
|
||||
__attribute__ ((__noreturn__, __format__ (__printf__, 3, 0)))
|
||||
{
|
||||
_warnerr(context, 1, code, 0, fmt, ap);
|
||||
abort();
|
||||
UNREACHABLE(return 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a warning to the log, default stderr, include the error from
|
||||
* the last failure and then abort.
|
||||
*
|
||||
* @param context A Kerberos 5 context
|
||||
* @param code error code of the last error
|
||||
* @param fmt message to print
|
||||
* @param ... arguments for format string
|
||||
*
|
||||
* @ingroup krb5_error
|
||||
*/
|
||||
|
||||
KRB5_LIB_NORETURN_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_abort(krb5_context context, krb5_error_code code, const char *fmt, ...)
|
||||
__attribute__ ((__noreturn__, __format__ (__printf__, 3, 4)))
|
||||
{
|
||||
FUNC_NORET(1, code, 0);
|
||||
abort();
|
||||
UNREACHABLE(return 0);
|
||||
}
|
||||
|
||||
KRB5_LIB_NORETURN_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_vabortx(krb5_context context, const char *fmt, va_list ap)
|
||||
__attribute__ ((__noreturn__, __format__ (__printf__, 2, 0)))
|
||||
{
|
||||
_warnerr(context, 0, 0, 0, fmt, ap);
|
||||
abort();
|
||||
UNREACHABLE(return 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a warning to the log, default stderr, and then abort.
|
||||
*
|
||||
* @param context A Kerberos 5 context
|
||||
* @param fmt printf format string of message to print
|
||||
* @param ... arguments for format string
|
||||
*
|
||||
* @ingroup krb5_error
|
||||
*/
|
||||
|
||||
KRB5_LIB_NORETURN_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_abortx(krb5_context context, const char *fmt, ...)
|
||||
__attribute__ ((__noreturn__, __format__ (__printf__, 2, 3)))
|
||||
{
|
||||
FUNC_NORET(0, 0, 0);
|
||||
abort();
|
||||
UNREACHABLE(return 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default logging facility.
|
||||
*
|
||||
* @param context A Kerberos 5 context
|
||||
* @param fac Facility to use for logging.
|
||||
*
|
||||
* @ingroup krb5_error
|
||||
*/
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
krb5_set_warn_dest(krb5_context context, krb5_log_facility *fac)
|
||||
{
|
||||
context->warn_dest = fac;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default logging facility.
|
||||
*
|
||||
* @param context A Kerberos 5 context
|
||||
*
|
||||
* @ingroup krb5_error
|
||||
*/
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_log_facility * KRB5_LIB_CALL
|
||||
krb5_get_warn_dest(krb5_context context)
|
||||
{
|
||||
return context->warn_dest;
|
||||
}
|
||||
Reference in New Issue
Block a user