1 /* 2 * Kerberos v5 authentication and ticket-passing routines. 3 * 4 * $FreeBSD: src/crypto/openssh/auth-krb5.c,v 1.6 2001/02/13 16:58:04 assar Exp $ 5 */ 6 /* 7 * Copyright (c) 2002 Daniel Kouril. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include "includes.h" 31 RCSID("$OpenBSD: auth-krb5.c,v 1.9 2002/09/09 06:48:06 itojun Exp $"); 32 33 #pragma ident "%Z%%M% %I% %E% SMI" 34 35 #include "ssh.h" 36 #include "ssh1.h" 37 #include "packet.h" 38 #include "xmalloc.h" 39 #include "log.h" 40 #include "servconf.h" 41 #include "uidswap.h" 42 #include "auth.h" 43 44 #ifdef KRB5 45 #include <krb5.h> 46 #ifndef HEIMDAL 47 #define krb5_get_err_text(context,code) error_message(code) 48 #endif /* !HEIMDAL */ 49 50 extern ServerOptions options; 51 52 static int 53 krb5_init(void *context) 54 { 55 Authctxt *authctxt = (Authctxt *)context; 56 krb5_error_code problem; 57 static int cleanup_registered = 0; 58 59 if (authctxt->krb5_ctx == NULL) { 60 problem = krb5_init_context(&authctxt->krb5_ctx); 61 if (problem) 62 return (problem); 63 krb5_init_ets(authctxt->krb5_ctx); 64 } 65 if (!cleanup_registered) { 66 fatal_add_cleanup(krb5_cleanup_proc, authctxt); 67 cleanup_registered = 1; 68 } 69 return (0); 70 } 71 72 /* 73 * Try krb5 authentication. server_user is passed for logging purposes 74 * only, in auth is received ticket, in client is returned principal 75 * from the ticket 76 */ 77 int 78 auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client, krb5_data *reply) 79 { 80 krb5_error_code problem; 81 krb5_principal server; 82 krb5_ticket *ticket; 83 int fd, ret; 84 85 ret = 0; 86 server = NULL; 87 ticket = NULL; 88 reply->length = 0; 89 90 problem = krb5_init(authctxt); 91 if (problem) 92 goto err; 93 94 problem = krb5_auth_con_init(authctxt->krb5_ctx, 95 &authctxt->krb5_auth_ctx); 96 if (problem) 97 goto err; 98 99 fd = packet_get_connection_in(); 100 #ifdef HEIMDAL 101 problem = krb5_auth_con_setaddrs_from_fd(authctxt->krb5_ctx, 102 authctxt->krb5_auth_ctx, &fd); 103 #else 104 problem = krb5_auth_con_genaddrs(authctxt->krb5_ctx, 105 authctxt->krb5_auth_ctx,fd, 106 KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR | 107 KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR); 108 #endif 109 if (problem) 110 goto err; 111 112 problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL , 113 KRB5_NT_SRV_HST, &server); 114 if (problem) 115 goto err; 116 117 problem = krb5_rd_req(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx, 118 auth, server, NULL, NULL, &ticket); 119 if (problem) 120 goto err; 121 122 #ifdef HEIMDAL 123 problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->client, 124 &authctxt->krb5_user); 125 #else 126 problem = krb5_copy_principal(authctxt->krb5_ctx, 127 ticket->enc_part2->client, 128 &authctxt->krb5_user); 129 #endif 130 if (problem) 131 goto err; 132 133 /* if client wants mutual auth */ 134 problem = krb5_mk_rep(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, 135 reply); 136 if (problem) 137 goto err; 138 139 /* Check .k5login authorization now. */ 140 if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, 141 authctxt->pw->pw_name)) 142 goto err; 143 144 if (client) 145 krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user, 146 client); 147 148 ret = 1; 149 err: 150 if (server) 151 krb5_free_principal(authctxt->krb5_ctx, server); 152 if (ticket) 153 krb5_free_ticket(authctxt->krb5_ctx, ticket); 154 if (!ret && reply->length) { 155 xfree(reply->data); 156 memset(reply, 0, sizeof(*reply)); 157 } 158 159 if (problem) { 160 if (authctxt->krb5_ctx != NULL) 161 debug("Kerberos v5 authentication failed: %s", 162 krb5_get_err_text(authctxt->krb5_ctx, problem)); 163 else 164 debug("Kerberos v5 authentication failed: %d", 165 problem); 166 } 167 168 return (ret); 169 } 170 171 int 172 auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt) 173 { 174 krb5_error_code problem; 175 krb5_ccache ccache = NULL; 176 char *pname; 177 krb5_creds **creds; 178 179 if (authctxt->pw == NULL || authctxt->krb5_user == NULL) 180 return (0); 181 182 temporarily_use_uid(authctxt->pw); 183 184 #ifdef HEIMDAL 185 problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_fcc_ops, &ccache); 186 #else 187 { 188 char ccname[40]; 189 int tmpfd; 190 191 snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid()); 192 193 if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) { 194 log("mkstemp(): %.100s", strerror(errno)); 195 problem = errno; 196 goto fail; 197 } 198 if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) { 199 log("fchmod(): %.100s", strerror(errno)); 200 close(tmpfd); 201 problem = errno; 202 goto fail; 203 } 204 close(tmpfd); 205 problem = krb5_cc_resolve(authctxt->krb5_ctx, ccname, &ccache); 206 } 207 #endif 208 if (problem) 209 goto fail; 210 211 problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache, 212 authctxt->krb5_user); 213 if (problem) 214 goto fail; 215 216 #ifdef HEIMDAL 217 problem = krb5_rd_cred2(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, 218 ccache, tgt); 219 if (problem) 220 goto fail; 221 #else 222 problem = krb5_rd_cred(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, 223 tgt, &creds, NULL); 224 if (problem) 225 goto fail; 226 problem = krb5_cc_store_cred(authctxt->krb5_ctx, ccache, *creds); 227 if (problem) 228 goto fail; 229 #endif 230 231 authctxt->krb5_fwd_ccache = ccache; 232 ccache = NULL; 233 234 authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); 235 236 problem = krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user, 237 &pname); 238 if (problem) 239 goto fail; 240 241 debug("Kerberos v5 TGT accepted (%s)", pname); 242 243 restore_uid(); 244 245 return (1); 246 247 fail: 248 if (problem) 249 debug("Kerberos v5 TGT passing failed: %s", 250 krb5_get_err_text(authctxt->krb5_ctx, problem)); 251 if (ccache) 252 krb5_cc_destroy(authctxt->krb5_ctx, ccache); 253 254 restore_uid(); 255 256 return (0); 257 } 258 259 int 260 auth_krb5_password(Authctxt *authctxt, const char *password) 261 { 262 #ifndef HEIMDAL 263 krb5_creds creds; 264 krb5_principal server; 265 char ccname[40]; 266 int tmpfd; 267 #endif 268 krb5_error_code problem; 269 270 if (authctxt->pw == NULL) 271 return (0); 272 273 temporarily_use_uid(authctxt->pw); 274 275 problem = krb5_init(authctxt); 276 if (problem) 277 goto out; 278 279 problem = krb5_parse_name(authctxt->krb5_ctx, authctxt->pw->pw_name, 280 &authctxt->krb5_user); 281 if (problem) 282 goto out; 283 284 #ifdef HEIMDAL 285 problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_mcc_ops, 286 &authctxt->krb5_fwd_ccache); 287 if (problem) 288 goto out; 289 290 problem = krb5_cc_initialize(authctxt->krb5_ctx, 291 authctxt->krb5_fwd_ccache, authctxt->krb5_user); 292 if (problem) 293 goto out; 294 295 restore_uid(); 296 problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user, 297 authctxt->krb5_fwd_ccache, password, 1, NULL); 298 temporarily_use_uid(authctxt->pw); 299 300 if (problem) 301 goto out; 302 303 #else 304 problem = krb5_get_init_creds_password(authctxt->krb5_ctx, &creds, 305 authctxt->krb5_user, (char *)password, NULL, NULL, 0, NULL, NULL); 306 if (problem) 307 goto out; 308 309 problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL, 310 KRB5_NT_SRV_HST, &server); 311 if (problem) 312 goto out; 313 314 restore_uid(); 315 problem = krb5_verify_init_creds(authctxt->krb5_ctx, &creds, server, 316 NULL, NULL, NULL); 317 krb5_free_principal(authctxt->krb5_ctx, server); 318 temporarily_use_uid(authctxt->pw); 319 if (problem) 320 goto out; 321 322 if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, 323 authctxt->pw->pw_name)) { 324 problem = -1; 325 goto out; 326 } 327 328 snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid()); 329 330 if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) { 331 log("mkstemp(): %.100s", strerror(errno)); 332 problem = errno; 333 goto out; 334 } 335 336 if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) { 337 log("fchmod(): %.100s", strerror(errno)); 338 close(tmpfd); 339 problem = errno; 340 goto out; 341 } 342 close(tmpfd); 343 344 problem = krb5_cc_resolve(authctxt->krb5_ctx, ccname, &authctxt->krb5_fwd_ccache); 345 if (problem) 346 goto out; 347 348 problem = krb5_cc_initialize(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache, 349 authctxt->krb5_user); 350 if (problem) 351 goto out; 352 353 problem= krb5_cc_store_cred(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache, 354 &creds); 355 if (problem) 356 goto out; 357 #endif 358 359 authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); 360 361 out: 362 restore_uid(); 363 364 if (problem) { 365 if (authctxt->krb5_ctx != NULL && problem!=-1) 366 debug("Kerberos password authentication failed: %s", 367 krb5_get_err_text(authctxt->krb5_ctx, problem)); 368 else 369 debug("Kerberos password authentication failed: %d", 370 problem); 371 372 krb5_cleanup_proc(authctxt); 373 374 if (options.kerberos_or_local_passwd) 375 return (-1); 376 else 377 return (0); 378 } 379 return (1); 380 } 381 382 void 383 krb5_cleanup_proc(void *context) 384 { 385 Authctxt *authctxt = (Authctxt *)context; 386 387 debug("krb5_cleanup_proc called"); 388 if (authctxt->krb5_fwd_ccache) { 389 krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); 390 authctxt->krb5_fwd_ccache = NULL; 391 } 392 if (authctxt->krb5_user) { 393 krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user); 394 authctxt->krb5_user = NULL; 395 } 396 if (authctxt->krb5_auth_ctx) { 397 krb5_auth_con_free(authctxt->krb5_ctx, 398 authctxt->krb5_auth_ctx); 399 authctxt->krb5_auth_ctx = NULL; 400 } 401 if (authctxt->krb5_ctx) { 402 krb5_free_context(authctxt->krb5_ctx); 403 authctxt->krb5_ctx = NULL; 404 } 405 } 406 407 #endif /* KRB5 */ 408