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