1 #pragma ident "%Z%%M% %I% %E% SMI" 2 /* 3 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 4 * Use is subject to license terms. 5 */ 6 7 #include <k5-int.h> 8 #include "int-proto.h" 9 10 extern krb5_error_code krb5_libdefault_boolean(); 11 12 static krb5_error_code 13 krb5_cc_copy_creds_except(krb5_context context, krb5_ccache incc, krb5_ccache outcc, krb5_principal princ) 14 { 15 krb5_error_code code; 16 krb5_flags flags; 17 krb5_cc_cursor cur; 18 krb5_creds creds; 19 20 flags = 0; /* turns off OPENCLOSE mode */ 21 if ((code = krb5_cc_set_flags(context, incc, flags)) != NULL) 22 return(code); 23 if ((code = krb5_cc_set_flags(context, outcc, flags)) != NULL) 24 return(code); 25 26 if ((code = krb5_cc_start_seq_get(context, incc, &cur)) != NULL) 27 goto cleanup; 28 29 while ((code = krb5_cc_next_cred(context, incc, &cur, &creds)) == NULL) { 30 if (krb5_principal_compare(context, princ, creds.server)) 31 continue; 32 33 code = krb5_cc_store_cred(context, outcc, &creds); 34 krb5_free_cred_contents(context, &creds); 35 if (code) 36 goto cleanup; 37 } 38 39 if (code != KRB5_CC_END) 40 goto cleanup; 41 42 code = 0; 43 44 cleanup: 45 flags = KRB5_TC_OPENCLOSE; 46 47 if (code) 48 (void) krb5_cc_set_flags(context, incc, flags); 49 else 50 code = krb5_cc_set_flags(context, incc, flags); 51 52 if (code) 53 (void) krb5_cc_set_flags(context, outcc, flags); 54 else 55 code = krb5_cc_set_flags(context, outcc, flags); 56 57 return(code); 58 } 59 60 krb5_error_code KRB5_CALLCONV 61 krb5_verify_init_creds(krb5_context context, 62 krb5_creds *creds, 63 krb5_principal server_arg, 64 krb5_keytab keytab_arg, 65 krb5_ccache *ccache_arg, 66 krb5_verify_init_creds_opt *options) 67 { 68 krb5_error_code ret; 69 krb5_principal server; 70 krb5_keytab keytab; 71 krb5_ccache ccache; 72 krb5_keytab_entry kte; 73 krb5_creds in_creds, *out_creds; 74 krb5_auth_context authcon; 75 krb5_data ap_req; 76 77 /* KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN */ 78 79 server = NULL; 80 keytab = NULL; 81 ccache = NULL; 82 out_creds = NULL; 83 authcon = NULL; 84 ap_req.data = NULL; 85 86 if (server_arg) 87 server = server_arg; 88 else if (ret = krb5_sname_to_principal(context, NULL, NULL, 89 KRB5_NT_SRV_HST, &server)) 90 goto cleanup; 91 92 /* first, check if the server is in the keytab. If not, there's 93 no reason to continue. rd_req does all this, but there's 94 no way to know that a given error is caused by a missing 95 keytab or key, and not by some other problem. */ 96 97 if (keytab_arg) { 98 keytab = keytab_arg; 99 } else { 100 /* Solaris Kerberos: ignore errors here, deal with below */ 101 ret = krb5_kt_default(context, &keytab); 102 } 103 104 /* Warning: be very, very careful when modifying the logic here */ 105 if (keytab == NULL || 106 (ret = krb5_kt_get_entry(context, keytab, server, 0, 0, &kte))) { 107 /* this means there is no keying material. This is ok, as long as 108 it is not prohibited by the configuration */ 109 110 int nofail = 1; /* Solaris Kerberos: default return error if keytab problems */ 111 112 if (options && 113 (options->flags & KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL)) { 114 /* first, if options are set then use the option value to set nofail */ 115 nofail = options->ap_req_nofail; 116 } else { 117 /* 118 * Check verify_ap_req_nofail if set in config file. Note this logic 119 * assumes that krb5_libdefault_boolean will not set nofail to a 120 * default value if verify_ap_req_nofail is not explictly set in 121 * config file. Don't care about the return code. 122 */ 123 (void) krb5_libdefault_boolean(context, &creds->client->realm, 124 "verify_ap_req_nofail", 125 &nofail); 126 } 127 /* Solaris Kerberos: exit without an error ONLY if nofail is false */ 128 if (!nofail) 129 ret = 0; 130 131 goto cleanup; 132 } 133 134 krb5_kt_free_entry(context, &kte); 135 136 /* If the creds are for the server principal, we're set, just do 137 a mk_req. Otherwise, do a get_credentials first. */ 138 139 if (krb5_principal_compare(context, server, creds->server)) { 140 /* make an ap_req */ 141 if ((ret = krb5_mk_req_extended(context, &authcon, 0, NULL, creds, 142 &ap_req))) 143 goto cleanup; 144 } else { 145 /* this is unclean, but it's the easiest way without ripping the 146 library into very small pieces. store the client's initial cred 147 in a memory ccache, then call the library. Later, we'll copy 148 everything except the initial cred into the ccache we return to 149 the user. A clean implementation would involve library 150 internals with a coherent idea of "in" and "out". */ 151 152 /* insert the initial cred into the ccache */ 153 154 if ((ret = krb5_cc_resolve(context, "MEMORY:rd_req", &ccache))) 155 goto cleanup; 156 157 if ((ret = krb5_cc_initialize(context, ccache, creds->client)) != NULL) 158 goto cleanup; 159 160 if ((ret = krb5_cc_store_cred(context, ccache, creds)) != NULL) 161 goto cleanup; 162 163 /* set up for get_creds */ 164 memset(&in_creds, 0, sizeof(in_creds)); 165 in_creds.client = creds->client; 166 in_creds.server = server; 167 if ((ret = krb5_timeofday(context, &in_creds.times.endtime))) 168 goto cleanup; 169 in_creds.times.endtime += 5*60; 170 171 if ((ret = krb5_get_credentials(context, 0, ccache, &in_creds, 172 &out_creds))) 173 goto cleanup; 174 175 /* make an ap_req */ 176 if ((ret = krb5_mk_req_extended(context, &authcon, 0, NULL, out_creds, 177 &ap_req))) 178 goto cleanup; 179 } 180 181 /* wipe the auth context for mk_req */ 182 if (authcon) { 183 krb5_auth_con_free(context, authcon); 184 authcon = NULL; 185 } 186 187 /* verify the ap_req */ 188 189 if ((ret = krb5_rd_req(context, &authcon, &ap_req, server, keytab, 190 NULL, NULL))) 191 goto cleanup; 192 193 /* if we get this far, then the verification succeeded. We can 194 still fail if the library stuff here fails, but that's it */ 195 196 if (ccache_arg && ccache) { 197 if (*ccache_arg == NULL) { 198 krb5_ccache retcc; 199 200 retcc = NULL; 201 202 if (((ret = krb5_cc_resolve(context, "MEMORY:rd_req2", &retcc)) != NULL) || 203 ((ret = krb5_cc_initialize(context, retcc, creds->client)) != NULL) || 204 ((ret = krb5_cc_copy_creds_except(context, ccache, retcc, 205 creds->server)) != NULL)) { 206 if (retcc) 207 (void) krb5_cc_destroy(context, retcc); 208 } else { 209 *ccache_arg = retcc; 210 } 211 } else { 212 ret = krb5_cc_copy_creds_except(context, ccache, *ccache_arg, 213 server); 214 } 215 } 216 217 /* if any of the above paths returned an errors, then ret is set 218 accordingly. either that, or it's zero, which is fine, too */ 219 220 cleanup: 221 if (!server_arg && server) 222 krb5_free_principal(context, server); 223 if (!keytab_arg && keytab) 224 (void) krb5_kt_close(context, keytab); 225 if (ccache) 226 (void) krb5_cc_destroy(context, ccache); 227 if (out_creds) 228 krb5_free_creds(context, out_creds); 229 if (authcon) 230 krb5_auth_con_free(context, authcon); 231 if (ap_req.data) 232 krb5_xfree(ap_req.data); 233 234 return(ret); 235 } 236