1 /* 2 * Kerberos v5 authentication and ticket-passing routines. 3 * 4 * $xFreeBSD: 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.8 2002/03/19 10:49:35 markus Exp $"); 32 33 #include "ssh.h" 34 #include "ssh1.h" 35 #include "packet.h" 36 #include "xmalloc.h" 37 #include "log.h" 38 #include "servconf.h" 39 #include "uidswap.h" 40 #include "auth.h" 41 42 #ifdef KRB5 43 #include <krb5.h> 44 #ifndef HEIMDAL 45 #define krb5_get_err_text(context,code) error_message(code) 46 #endif /* !HEIMDAL */ 47 48 extern ServerOptions options; 49 50 static int 51 krb5_init(void *context) 52 { 53 Authctxt *authctxt = (Authctxt *)context; 54 krb5_error_code problem; 55 static int cleanup_registered = 0; 56 57 if (authctxt->krb5_ctx == NULL) { 58 problem = krb5_init_context(&authctxt->krb5_ctx); 59 if (problem) 60 return (problem); 61 krb5_init_ets(authctxt->krb5_ctx); 62 } 63 if (!cleanup_registered) { 64 fatal_add_cleanup(krb5_cleanup_proc, authctxt); 65 cleanup_registered = 1; 66 } 67 return (0); 68 } 69 70 /* 71 * Try krb5 authentication. server_user is passed for logging purposes 72 * only, in auth is received ticket, in client is returned principal 73 * from the ticket 74 */ 75 int 76 auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client) 77 { 78 krb5_error_code problem; 79 krb5_principal server; 80 krb5_data reply; 81 krb5_ticket *ticket; 82 int fd, ret; 83 84 ret = 0; 85 server = NULL; 86 ticket = NULL; 87 reply.length = 0; 88 89 problem = krb5_init(authctxt); 90 if (problem) 91 goto err; 92 93 problem = krb5_auth_con_init(authctxt->krb5_ctx, 94 &authctxt->krb5_auth_ctx); 95 if (problem) 96 goto err; 97 98 fd = packet_get_connection_in(); 99 #ifdef HEIMDAL 100 problem = krb5_auth_con_setaddrs_from_fd(authctxt->krb5_ctx, 101 authctxt->krb5_auth_ctx, &fd); 102 #else 103 problem = krb5_auth_con_genaddrs(authctxt->krb5_ctx, 104 authctxt->krb5_auth_ctx,fd, 105 KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR | 106 KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR); 107 #endif 108 if (problem) 109 goto err; 110 111 problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL , 112 KRB5_NT_SRV_HST, &server); 113 if (problem) 114 goto err; 115 116 problem = krb5_rd_req(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx, 117 auth, server, NULL, NULL, &ticket); 118 if (problem) 119 goto err; 120 121 #ifdef HEIMDAL 122 problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->client, 123 &authctxt->krb5_user); 124 #else 125 problem = krb5_copy_principal(authctxt->krb5_ctx, 126 ticket->enc_part2->client, 127 &authctxt->krb5_user); 128 #endif 129 if (problem) 130 goto err; 131 132 /* if client wants mutual auth */ 133 problem = krb5_mk_rep(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, 134 &reply); 135 if (problem) 136 goto err; 137 138 /* Check .k5login authorization now. */ 139 if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, 140 authctxt->pw->pw_name)) 141 goto err; 142 143 if (client) 144 krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user, 145 client); 146 147 packet_start(SSH_SMSG_AUTH_KERBEROS_RESPONSE); 148 packet_put_string((char *) reply.data, reply.length); 149 packet_send(); 150 packet_write_wait(); 151 152 ret = 1; 153 err: 154 if (server) 155 krb5_free_principal(authctxt->krb5_ctx, server); 156 if (ticket) 157 krb5_free_ticket(authctxt->krb5_ctx, ticket); 158 if (reply.length) 159 xfree(reply.data); 160 161 if (problem) { 162 if (authctxt->krb5_ctx != NULL) 163 debug("Kerberos v5 authentication failed: %s", 164 krb5_get_err_text(authctxt->krb5_ctx, problem)); 165 else 166 debug("Kerberos v5 authentication failed: %d", 167 problem); 168 } 169 170 return (ret); 171 } 172 173 int 174 auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt) 175 { 176 krb5_error_code problem; 177 krb5_ccache ccache = NULL; 178 char *pname; 179 krb5_creds **creds; 180 181 if (authctxt->pw == NULL || authctxt->krb5_user == NULL) 182 return (0); 183 184 temporarily_use_uid(authctxt->pw); 185 186 #ifdef HEIMDAL 187 problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_fcc_ops, &ccache); 188 #else 189 { 190 char ccname[40]; 191 int tmpfd; 192 193 snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid()); 194 195 if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) { 196 log("mkstemp(): %.100s", strerror(errno)); 197 problem = errno; 198 goto fail; 199 } 200 if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) { 201 log("fchmod(): %.100s", strerror(errno)); 202 close(tmpfd); 203 problem = errno; 204 goto fail; 205 } 206 close(tmpfd); 207 problem = krb5_cc_resolve(authctxt->krb5_ctx, ccname, &ccache); 208 } 209 #endif 210 if (problem) 211 goto fail; 212 213 problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache, 214 authctxt->krb5_user); 215 if (problem) 216 goto fail; 217 218 #ifdef HEIMDAL 219 problem = krb5_rd_cred2(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, 220 ccache, tgt); 221 if (problem) 222 goto fail; 223 #else 224 problem = krb5_rd_cred(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, 225 tgt, &creds, NULL); 226 if (problem) 227 goto fail; 228 problem = krb5_cc_store_cred(authctxt->krb5_ctx, ccache, *creds); 229 if (problem) 230 goto fail; 231 #endif 232 233 authctxt->krb5_fwd_ccache = ccache; 234 ccache = NULL; 235 236 authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); 237 238 problem = krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user, 239 &pname); 240 if (problem) 241 goto fail; 242 243 debug("Kerberos v5 TGT accepted (%s)", pname); 244 245 restore_uid(); 246 247 return (1); 248 249 fail: 250 if (problem) 251 debug("Kerberos v5 TGT passing failed: %s", 252 krb5_get_err_text(authctxt->krb5_ctx, problem)); 253 if (ccache) 254 krb5_cc_destroy(authctxt->krb5_ctx, ccache); 255 256 restore_uid(); 257 258 return (0); 259 } 260 261 int 262 auth_krb5_password(Authctxt *authctxt, const char *password) 263 { 264 #ifndef HEIMDAL 265 krb5_creds creds; 266 krb5_principal server; 267 char ccname[40]; 268 int tmpfd; 269 #endif 270 krb5_error_code problem; 271 272 if (authctxt->pw == NULL) 273 return (0); 274 275 temporarily_use_uid(authctxt->pw); 276 277 problem = krb5_init(authctxt); 278 if (problem) 279 goto out; 280 281 problem = krb5_parse_name(authctxt->krb5_ctx, authctxt->pw->pw_name, 282 &authctxt->krb5_user); 283 if (problem) 284 goto out; 285 286 #ifdef HEIMDAL 287 problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_mcc_ops, 288 &authctxt->krb5_fwd_ccache); 289 if (problem) 290 goto out; 291 292 problem = krb5_cc_initialize(authctxt->krb5_ctx, 293 authctxt->krb5_fwd_ccache, authctxt->krb5_user); 294 if (problem) 295 goto out; 296 297 restore_uid(); 298 problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user, 299 authctxt->krb5_fwd_ccache, password, 1, NULL); 300 temporarily_use_uid(authctxt->pw); 301 302 if (problem) 303 goto out; 304 305 #else 306 problem = krb5_get_init_creds_password(authctxt->krb5_ctx, &creds, 307 authctxt->krb5_user, (char *)password, NULL, NULL, 0, NULL, NULL); 308 if (problem) 309 goto out; 310 311 problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL, 312 KRB5_NT_SRV_HST, &server); 313 if (problem) 314 goto out; 315 316 restore_uid(); 317 problem = krb5_verify_init_creds(authctxt->krb5_ctx, &creds, server, 318 NULL, NULL, NULL); 319 krb5_free_principal(authctxt->krb5_ctx, server); 320 temporarily_use_uid(authctxt->pw); 321 if (problem) 322 goto out; 323 324 if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, 325 authctxt->pw->pw_name)) { 326 problem = -1; 327 goto out; 328 } 329 330 snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid()); 331 332 if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) { 333 log("mkstemp(): %.100s", strerror(errno)); 334 problem = errno; 335 goto out; 336 } 337 338 if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) { 339 log("fchmod(): %.100s", strerror(errno)); 340 close(tmpfd); 341 problem = errno; 342 goto out; 343 } 344 close(tmpfd); 345 346 problem = krb5_cc_resolve(authctxt->krb5_ctx, ccname, &authctxt->krb5_fwd_ccache); 347 if (problem) 348 goto out; 349 350 problem = krb5_cc_initialize(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache, 351 authctxt->krb5_user); 352 if (problem) 353 goto out; 354 355 problem= krb5_cc_store_cred(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache, 356 &creds); 357 if (problem) 358 goto out; 359 #endif 360 361 authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); 362 363 out: 364 restore_uid(); 365 366 if (problem) { 367 if (authctxt->krb5_ctx != NULL && problem!=-1) 368 debug("Kerberos password authentication failed: %s", 369 krb5_get_err_text(authctxt->krb5_ctx, problem)); 370 else 371 debug("Kerberos password authentication failed: %d", 372 problem); 373 374 krb5_cleanup_proc(authctxt); 375 376 if (options.kerberos_or_local_passwd) 377 return (-1); 378 else 379 return (0); 380 } 381 return (1); 382 } 383 384 void 385 krb5_cleanup_proc(void *context) 386 { 387 Authctxt *authctxt = (Authctxt *)context; 388 389 debug("krb5_cleanup_proc called"); 390 if (authctxt->krb5_fwd_ccache) { 391 krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); 392 authctxt->krb5_fwd_ccache = NULL; 393 } 394 if (authctxt->krb5_user) { 395 krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user); 396 authctxt->krb5_user = NULL; 397 } 398 if (authctxt->krb5_auth_ctx) { 399 krb5_auth_con_free(authctxt->krb5_ctx, 400 authctxt->krb5_auth_ctx); 401 authctxt->krb5_auth_ctx = NULL; 402 } 403 if (authctxt->krb5_ctx) { 404 krb5_free_context(authctxt->krb5_ctx); 405 authctxt->krb5_ctx = NULL; 406 } 407 } 408 409 #endif /* KRB5 */ 410