1 /* 2 * Portability glue functions for Kerberos. 3 * 4 * This file provides definitions of the interfaces that portable/krb5.h 5 * ensures exist if the function wasn't available in the Kerberos libraries. 6 * Everything in this file will be protected by #ifndef. If the native 7 * Kerberos libraries are fully capable, this file will be skipped. 8 * 9 * The canonical version of this file is maintained in the rra-c-util package, 10 * which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>. 11 * 12 * Written by Russ Allbery <eagle@eyrie.org> 13 * Copyright 2015-2016, 2018 Russ Allbery <eagle@eyrie.org> 14 * Copyright 2010-2012, 2014 15 * The Board of Trustees of the Leland Stanford Junior University 16 * 17 * Copying and distribution of this file, with or without modification, are 18 * permitted in any medium without royalty provided the copyright notice and 19 * this notice are preserved. This file is offered as-is, without any 20 * warranty. 21 * 22 * SPDX-License-Identifier: FSFAP 23 */ 24 25 #include <config.h> 26 #include <portable/krb5.h> 27 #include <portable/macros.h> 28 #include <portable/system.h> 29 30 #include <errno.h> 31 32 /* Figure out what header files to include for error reporting. */ 33 #if !defined(HAVE_KRB5_GET_ERROR_MESSAGE) && !defined(HAVE_KRB5_GET_ERR_TEXT) 34 # if !defined(HAVE_KRB5_GET_ERROR_STRING) 35 # if defined(HAVE_IBM_SVC_KRB5_SVC_H) 36 # include <ibm_svc/krb5_svc.h> 37 # elif defined(HAVE_ET_COM_ERR_H) 38 # include <et/com_err.h> 39 # elif defined(HAVE_KERBEROSV5_COM_ERR_H) 40 # include <kerberosv5/com_err.h> 41 # else 42 # include <com_err.h> 43 # endif 44 # endif 45 #endif 46 47 /* Used for unused parameters to silence gcc warnings. */ 48 #define UNUSED __attribute__((__unused__)) 49 50 /* 51 * This string is returned for unknown error messages. We use a static 52 * variable so that we can be sure not to free it. 53 */ 54 #if !defined(HAVE_KRB5_GET_ERROR_MESSAGE) \ 55 || !defined(HAVE_KRB5_FREE_ERROR_MESSAGE) 56 static const char error_unknown[] = "unknown error"; 57 #endif 58 59 60 #ifndef HAVE_KRB5_CC_GET_FULL_NAME 61 /* 62 * Given a Kerberos ticket cache, return the full name (TYPE:name) in 63 * newly-allocated memory. Returns an error code. Avoid asprintf and 64 * snprintf here in case someone wants to use this code without the rest of 65 * the portability layer. 66 */ 67 krb5_error_code 68 krb5_cc_get_full_name(krb5_context ctx, krb5_ccache ccache, char **out) 69 { 70 const char *type, *name; 71 size_t length; 72 73 type = krb5_cc_get_type(ctx, ccache); 74 if (type == NULL) 75 type = "FILE"; 76 name = krb5_cc_get_name(ctx, ccache); 77 if (name == NULL) 78 return EINVAL; 79 length = strlen(type) + 1 + strlen(name) + 1; 80 *out = malloc(length); 81 if (*out == NULL) 82 return errno; 83 sprintf(*out, "%s:%s", type, name); 84 return 0; 85 } 86 #endif /* !HAVE_KRB5_CC_GET_FULL_NAME */ 87 88 89 #ifndef HAVE_KRB5_GET_ERROR_MESSAGE 90 /* 91 * Given a Kerberos error code, return the corresponding error. Prefer the 92 * Kerberos interface if available since it will provide context-specific 93 * error information, whereas the error_message() call will only provide a 94 * fixed message. 95 */ 96 const char * 97 krb5_get_error_message(krb5_context ctx UNUSED, krb5_error_code code UNUSED) 98 { 99 const char *msg; 100 101 # if defined(HAVE_KRB5_GET_ERROR_STRING) 102 msg = krb5_get_error_string(ctx); 103 # elif defined(HAVE_KRB5_GET_ERR_TEXT) 104 msg = krb5_get_err_text(ctx, code); 105 # elif defined(HAVE_KRB5_SVC_GET_MSG) 106 krb5_svc_get_msg(code, (char **) &msg); 107 # else 108 msg = error_message(code); 109 # endif 110 if (msg == NULL) 111 return error_unknown; 112 else 113 return msg; 114 } 115 #endif /* !HAVE_KRB5_GET_ERROR_MESSAGE */ 116 117 118 #ifndef HAVE_KRB5_FREE_ERROR_MESSAGE 119 /* 120 * Free an error string if necessary. If we returned a static string, make 121 * sure we don't free it. 122 * 123 * This code assumes that the set of implementations that have 124 * krb5_free_error_message is a subset of those with krb5_get_error_message. 125 * If this assumption ever breaks, we may call the wrong free function. 126 */ 127 void 128 krb5_free_error_message(krb5_context ctx UNUSED, const char *msg) 129 { 130 if (msg == error_unknown) 131 return; 132 # if defined(HAVE_KRB5_GET_ERROR_STRING) 133 krb5_free_error_string(ctx, (char *) msg); 134 # elif defined(HAVE_KRB5_SVC_GET_MSG) 135 krb5_free_string(ctx, (char *) msg); 136 # endif 137 } 138 #endif /* !HAVE_KRB5_FREE_ERROR_MESSAGE */ 139 140 141 #ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC 142 /* 143 * Allocate and initialize a krb5_get_init_creds_opt struct. This code 144 * assumes that an all-zero bit pattern will create a NULL pointer. 145 */ 146 krb5_error_code 147 krb5_get_init_creds_opt_alloc(krb5_context ctx UNUSED, 148 krb5_get_init_creds_opt **opts) 149 { 150 *opts = calloc(1, sizeof(krb5_get_init_creds_opt)); 151 if (*opts == NULL) 152 return errno; 153 krb5_get_init_creds_opt_init(*opts); 154 return 0; 155 } 156 #endif /* !HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC */ 157 158 159 #ifndef HAVE_KRB5_PRINCIPAL_GET_REALM 160 /* 161 * Return the realm of a principal as a const char *. 162 */ 163 const char * 164 krb5_principal_get_realm(krb5_context ctx UNUSED, krb5_const_principal princ) 165 { 166 const krb5_data *data; 167 168 data = krb5_princ_realm(ctx, princ); 169 if (data == NULL || data->data == NULL) 170 return NULL; 171 return data->data; 172 } 173 #endif /* !HAVE_KRB5_PRINCIPAL_GET_REALM */ 174 175 176 #ifndef HAVE_KRB5_VERIFY_INIT_CREDS_OPT_INIT 177 /* 178 * Initialize the option struct for krb5_verify_init_creds. 179 */ 180 void 181 krb5_verify_init_creds_opt_init(krb5_verify_init_creds_opt *opt) 182 { 183 opt->flags = 0; 184 opt->ap_req_nofail = 0; 185 } 186 #endif 187