1/* $OpenBSD: gss-serv-krb5.c,v 1.7 2006/08/03 03:34:42 deraadt Exp $ */ 2 3/* 4 * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "includes.h" 28 29#ifdef GSSAPI 30#ifdef KRB5 31 32#include <sys/types.h> 33 34#include <stdarg.h> 35#include <string.h> 36 37#include "xmalloc.h" 38#include "key.h" 39#include "hostfile.h" 40#include "auth.h" 41#include "log.h" 42#include "servconf.h" 43 44#include "buffer.h" 45#include "ssh-gss.h" 46 47extern ServerOptions options; 48 49#ifdef HEIMDAL 50# include <krb5.h> 51#else 52# ifdef HAVE_GSSAPI_KRB5_H 53# include <gssapi_krb5.h> 54# elif HAVE_GSSAPI_GSSAPI_KRB5_H 55# include <gssapi/gssapi_krb5.h> 56# endif 57#endif 58 59#ifdef __APPLE_CROSS_REALM__ 60#include <membership.h> 61#endif 62 63static krb5_context krb_context = NULL; 64 65/* Initialise the krb5 library, for the stuff that GSSAPI won't do */ 66 67static int 68ssh_gssapi_krb5_init(void) 69{ 70 krb5_error_code problem; 71 72 if (krb_context != NULL) 73 return 1; 74 75 problem = krb5_init_context(&krb_context); 76 if (problem) { 77 logit("Cannot initialize krb5 context"); 78 return 0; 79 } 80 81 return 1; 82} 83 84#ifdef __APPLE_CROSS_REALM__ 85/* Check if the principal matches any of the user's OD entries for RecordName */ 86krb5_boolean 87od_kuserok(krb5_context context, krb5_principal principal, const char *luser) 88{ 89 char *kprinc = NULL; 90 int ret = 0, retval = FALSE; 91 uuid_t krb_uuid, un_uuid; 92 93 ret = krb5_unparse_name(context, principal, &kprinc); 94 if (!ret) { 95 logit("od_kuserok - krb5_unparse_name failed: %d", ret); 96 goto error; 97 } 98 99 ret = mbr_identifier_to_uuid(ID_TYPE_USERNAME, luser, strlen(luser), un_uuid); 100 if (!ret) { 101 logit("od_kuserok - mbr_identifier_to_uuid: %d", ret); 102 goto error; 103 } 104 105 ret = mbr_identifier_to_uuid(ID_TYPE_KERBEROS, kprinc, strlen(kprinc), krb_uuid); 106 if (!ret) { 107 goto error; 108 } 109 110 ret = uuid_compare(krb_uuid, un_uuid); 111 if (0 == ret) { 112 retval = TRUE; 113 } 114 115error: 116 if (kprinc) 117 free(kprinc); 118 119 return retval; 120} 121#endif 122 123/* Check if this user is OK to login. This only works with krb5 - other 124 * GSSAPI mechanisms will need their own. 125 * Returns true if the user is OK to log in, otherwise returns 0 126 */ 127 128static int 129ssh_gssapi_krb5_userok(ssh_gssapi_client *client, char *name) 130{ 131 krb5_principal princ; 132 int retval; 133 134 if (ssh_gssapi_krb5_init() == 0) 135 return 0; 136 137 if ((retval = krb5_parse_name(krb_context, client->exportedname.value, 138 &princ))) { 139 logit("krb5_parse_name(): %.100s", 140 krb5_get_err_text(krb_context, retval)); 141 return 0; 142 } 143 if (krb5_kuserok(krb_context, princ, name)) { 144 retval = 1; 145 logit("Authorized to %s, krb5 principal %s (krb5_kuserok)", 146 name, (char *)client->displayname.value); 147#ifdef __APPLE_CROSS_REALM__ 148 } else if (od_kuserok(krb_context, princ, name)) { 149 retval = 1; 150 logit("Authorized to %s, krb5 principal %s (od_kuserok)", 151 name, (char *)client->displayname.value); 152#endif 153 } else 154 retval = 0; 155 156 krb5_free_principal(krb_context, princ); 157 return retval; 158} 159 160 161/* This writes out any forwarded credentials from the structure populated 162 * during userauth. Called after we have setuid to the user */ 163 164static void 165ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) 166{ 167 krb5_ccache ccache; 168 krb5_error_code problem; 169 krb5_principal princ; 170 OM_uint32 maj_status, min_status; 171 int len; 172 const char *new_ccname; 173 174 if (client->creds == NULL) { 175 debug("No credentials stored"); 176 return; 177 } 178 179 if (ssh_gssapi_krb5_init() == 0) 180 return; 181 182#ifdef HEIMDAL 183 if ((problem = krb5_cc_gen_new(krb_context, &krb5_acc_ops, &ccache))) { 184 logit("krb5_cc_gen_new(): %.100s", 185 krb5_get_err_text(krb_context, problem)); 186 return; 187 } 188#else 189 if ((problem = ssh_krb5_cc_gen(krb_context, &ccache))) { 190 logit("ssh_krb5_cc_gen(): %.100s", 191 krb5_get_err_text(krb_context, problem)); 192 return; 193 } 194#endif /* #ifdef HEIMDAL */ 195 196 if ((problem = krb5_parse_name(krb_context, 197 client->exportedname.value, &princ))) { 198 logit("krb5_parse_name(): %.100s", 199 krb5_get_err_text(krb_context, problem)); 200 krb5_cc_destroy(krb_context, ccache); 201 return; 202 } 203 204 if ((problem = krb5_cc_initialize(krb_context, ccache, princ))) { 205 logit("krb5_cc_initialize(): %.100s", 206 krb5_get_err_text(krb_context, problem)); 207 krb5_free_principal(krb_context, princ); 208 krb5_cc_destroy(krb_context, ccache); 209 return; 210 } 211 212 krb5_free_principal(krb_context, princ); 213 214 if ((maj_status = gss_krb5_copy_ccache(&min_status, 215 client->creds, ccache))) { 216 logit("gss_krb5_copy_ccache() failed"); 217 krb5_cc_destroy(krb_context, ccache); 218 return; 219 } 220 221 new_ccname = krb5_cc_get_name(krb_context, ccache); 222 223 client->store.envvar = "KRB5CCNAME"; 224#ifdef USE_CCAPI 225 xasprintf(&client->store.envval, "API:%s", new_ccname); 226 client->store.filename = NULL; 227#else 228 xasprintf(&client->store.envval, "FILE:%s", new_ccname); 229 client->store.filename = xstrdup(new_ccname); 230#endif 231 232#ifdef USE_PAM 233 if (options.use_pam) 234 do_pam_putenv(client->store.envvar, client->store.envval); 235#endif 236 237 krb5_cc_close(krb_context, ccache); 238 239 return; 240} 241 242int 243ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store, 244 ssh_gssapi_client *client) 245{ 246 krb5_ccache ccache = NULL; 247 krb5_principal principal = NULL; 248 char *name = NULL; 249 krb5_error_code problem; 250 OM_uint32 maj_status, min_status; 251 252 if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) { 253 logit("krb5_cc_resolve(): %.100s", 254 krb5_get_err_text(krb_context, problem)); 255 return 0; 256 } 257 258 /* Find out who the principal in this cache is */ 259 if ((problem = krb5_cc_get_principal(krb_context, ccache, 260 &principal))) { 261 logit("krb5_cc_get_principal(): %.100s", 262 krb5_get_err_text(krb_context, problem)); 263 krb5_cc_close(krb_context, ccache); 264 return 0; 265 } 266 267 if ((problem = krb5_unparse_name(krb_context, principal, &name))) { 268 logit("krb5_unparse_name(): %.100s", 269 krb5_get_err_text(krb_context, problem)); 270 krb5_free_principal(krb_context, principal); 271 krb5_cc_close(krb_context, ccache); 272 return 0; 273 } 274 275 276 if (strcmp(name,client->exportedname.value)!=0) { 277 debug("Name in local credentials cache differs. Not storing"); 278 krb5_free_principal(krb_context, principal); 279 krb5_cc_close(krb_context, ccache); 280 krb5_free_unparsed_name(krb_context, name); 281 return 0; 282 } 283 krb5_free_unparsed_name(krb_context, name); 284 285 /* Name matches, so lets get on with it! */ 286 287 if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) { 288 logit("krb5_cc_initialize(): %.100s", 289 krb5_get_err_text(krb_context, problem)); 290 krb5_free_principal(krb_context, principal); 291 krb5_cc_close(krb_context, ccache); 292 return 0; 293 } 294 295 krb5_free_principal(krb_context, principal); 296 297 if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds, 298 ccache))) { 299 logit("gss_krb5_copy_ccache() failed. Sorry!"); 300 krb5_cc_close(krb_context, ccache); 301 return 0; 302 } 303 304 return 1; 305} 306 307ssh_gssapi_mech gssapi_kerberos_mech = { 308 "toWM5Slw5Ew8Mqkay+al2g==", 309 "Kerberos", 310 {9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"}, 311 NULL, 312 &ssh_gssapi_krb5_userok, 313 NULL, 314 &ssh_gssapi_krb5_storecreds, 315 &ssh_gssapi_krb5_updatecreds 316}; 317 318#endif /* KRB5 */ 319 320#endif /* GSSAPI */ 321