auth-krb5.c revision 1.7
1/* $NetBSD: auth-krb5.c,v 1.7 2015/04/03 23:58:19 christos Exp $ */ 2/* $OpenBSD: auth-krb5.c,v 1.20 2013/07/20 01:55:13 djm Exp $ */ 3/* 4 * Kerberos v5 authentication and ticket-passing routines. 5 * 6 * $FreeBSD: src/crypto/openssh/auth-krb5.c,v 1.6 2001/02/13 16:58:04 assar Exp $ 7 */ 8/* 9 * Copyright (c) 2002 Daniel Kouril. All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include "includes.h" 33__RCSID("$NetBSD: auth-krb5.c,v 1.7 2015/04/03 23:58:19 christos Exp $"); 34#include <sys/types.h> 35#include <pwd.h> 36#include <stdarg.h> 37#include <string.h> 38 39#include "xmalloc.h" 40#include "ssh.h" 41#include "ssh1.h" 42#include "packet.h" 43#include "log.h" 44#include "buffer.h" 45#include "misc.h" 46#include "servconf.h" 47#include "uidswap.h" 48#include "key.h" 49#include "hostfile.h" 50#include "auth.h" 51 52#ifdef KRB5 53#include <krb5.h> 54 55extern ServerOptions options; 56 57static int 58krb5_init(void *context) 59{ 60 Authctxt *authctxt = (Authctxt *)context; 61 krb5_error_code problem; 62 63 if (authctxt->krb5_ctx == NULL) { 64 problem = krb5_init_context(&authctxt->krb5_ctx); 65 if (problem) 66 return (problem); 67 krb5_init_ets(authctxt->krb5_ctx); 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 */ 77int 78auth_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 const char *errtxt; 85 86 ret = 0; 87 server = NULL; 88 ticket = NULL; 89 reply->length = 0; 90 91 problem = krb5_init(authctxt); 92 if (problem) 93 goto err; 94 95 problem = krb5_auth_con_init(authctxt->krb5_ctx, 96 &authctxt->krb5_auth_ctx); 97 if (problem) 98 goto err; 99 100 fd = packet_get_connection_in(); 101 problem = krb5_auth_con_setaddrs_from_fd(authctxt->krb5_ctx, 102 authctxt->krb5_auth_ctx, &fd); 103 if (problem) 104 goto err; 105 106 problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL, 107 KRB5_NT_SRV_HST, &server); 108 if (problem) 109 goto err; 110 111 problem = krb5_rd_req(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx, 112 auth, server, NULL, NULL, &ticket); 113 if (problem) 114 goto err; 115 116 problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->client, 117 &authctxt->krb5_user); 118 if (problem) 119 goto err; 120 121 /* if client wants mutual auth */ 122 problem = krb5_mk_rep(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, 123 reply); 124 if (problem) 125 goto err; 126 127 /* Check .k5login authorization now. */ 128 if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, 129 authctxt->pw->pw_name)) 130 goto err; 131 132 if (client) 133 krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user, 134 client); 135 136 ret = 1; 137 err: 138 if (server) 139 krb5_free_principal(authctxt->krb5_ctx, server); 140 if (ticket) 141 krb5_free_ticket(authctxt->krb5_ctx, ticket); 142 if (!ret && reply->length) { 143 free(reply->data); 144 memset(reply, 0, sizeof(*reply)); 145 } 146 147 if (problem) { 148 errtxt = NULL; 149 if (authctxt->krb5_ctx != NULL) 150 errtxt = krb5_get_error_message(authctxt->krb5_ctx, 151 problem); 152 if (errtxt != NULL) { 153 debug("Kerberos v5 authentication failed: %s", errtxt); 154 krb5_free_error_message(authctxt->krb5_ctx, errtxt); 155 } else 156 debug("Kerberos v5 authentication failed: %d", 157 problem); 158 } 159 160 return (ret); 161} 162 163int 164auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt) 165{ 166 krb5_error_code problem; 167 krb5_ccache ccache = NULL; 168 char *pname; 169 const char *errtxt; 170 171 if (authctxt->pw == NULL || authctxt->krb5_user == NULL) 172 return (0); 173 174 temporarily_use_uid(authctxt->pw); 175 176 problem = krb5_cc_new_unique(authctxt->krb5_ctx, "FILE", NULL, &ccache); 177 if (problem) 178 goto fail; 179 180 problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache, 181 authctxt->krb5_user); 182 if (problem) 183 goto fail; 184 185 problem = krb5_rd_cred2(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, 186 ccache, tgt); 187 if (problem) 188 goto fail; 189 190 authctxt->krb5_fwd_ccache = ccache; 191 ccache = NULL; 192 193 authctxt->krb5_ticket_file = __UNCONST(krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache)); 194 195 problem = krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user, 196 &pname); 197 if (problem) 198 goto fail; 199 200#ifdef USE_PAM 201 if (options.use_pam) 202 do_pam_putenv(__UNCONST("KRB5CCNAME"), authctxt->krb5_ticket_file); 203#endif 204 debug("Kerberos v5 TGT accepted (%s)", pname); 205 206 restore_uid(); 207 208 return (1); 209 210 fail: 211 if (problem) { 212 errtxt = krb5_get_error_message(authctxt->krb5_ctx, problem); 213 if (errtxt != NULL) { 214 debug("Kerberos v5 TGT passing failed: %s", errtxt); 215 krb5_free_error_message(authctxt->krb5_ctx, errtxt); 216 } else 217 debug("Kerberos v5 TGT passing failed: %d", problem); 218 } 219 if (ccache) 220 krb5_cc_destroy(authctxt->krb5_ctx, ccache); 221 222 restore_uid(); 223 224 return (0); 225} 226 227 228int 229auth_krb5_password(Authctxt *authctxt, const char *password) 230{ 231 krb5_error_code problem; 232 krb5_ccache ccache = NULL; 233 const char *errmsg; 234 235 temporarily_use_uid(authctxt->pw); 236 237 problem = krb5_init(authctxt); 238 if (problem) 239 goto out; 240 241 problem = krb5_parse_name(authctxt->krb5_ctx, authctxt->pw->pw_name, 242 &authctxt->krb5_user); 243 if (problem) 244 goto out; 245 246 problem = krb5_cc_new_unique(authctxt->krb5_ctx, 247 krb5_mcc_ops.prefix, NULL, &ccache); 248 if (problem) 249 goto out; 250 251 problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache, 252 authctxt->krb5_user); 253 if (problem) 254 goto out; 255 256 restore_uid(); 257 258 problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user, 259 ccache, password, 1, NULL); 260 261 temporarily_use_uid(authctxt->pw); 262 263 if (problem) 264 goto out; 265 266 problem = krb5_cc_new_unique(authctxt->krb5_ctx, 267 krb5_fcc_ops.prefix, NULL, &authctxt->krb5_fwd_ccache); 268 if (problem) 269 goto out; 270 271 problem = krb5_cc_copy_cache(authctxt->krb5_ctx, ccache, 272 authctxt->krb5_fwd_ccache); 273 krb5_cc_destroy(authctxt->krb5_ctx, ccache); 274 ccache = NULL; 275 if (problem) 276 goto out; 277 278 authctxt->krb5_ticket_file = __UNCONST(krb5_cc_get_name( 279 authctxt->krb5_ctx, authctxt->krb5_fwd_ccache)); 280 281 out: 282 restore_uid(); 283 284 if (problem) { 285 if (ccache) 286 krb5_cc_destroy(authctxt->krb5_ctx, ccache); 287 288 if (authctxt->krb5_ctx != NULL) { 289 errmsg = krb5_get_error_message(authctxt->krb5_ctx, 290 problem); 291 debug("Kerberos password authentication failed: %s", 292 errmsg); 293 krb5_free_error_message(authctxt->krb5_ctx, errmsg); 294 } else 295 debug("Kerberos password authentication failed: %d", 296 problem); 297 298 krb5_cleanup_proc(authctxt); 299 300 if (options.kerberos_or_local_passwd) 301 return (-1); 302 else 303 return (0); 304 } 305 return (authctxt->valid ? 1 : 0); 306} 307 308void 309krb5_cleanup_proc(Authctxt *authctxt) 310{ 311 debug("krb5_cleanup_proc called"); 312 if (authctxt->krb5_fwd_ccache) { 313 krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); 314 authctxt->krb5_fwd_ccache = NULL; 315 } 316 if (authctxt->krb5_user) { 317 krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user); 318 authctxt->krb5_user = NULL; 319 } 320 if (authctxt->krb5_auth_ctx) { 321 krb5_auth_con_free(authctxt->krb5_ctx, 322 authctxt->krb5_auth_ctx); 323 authctxt->krb5_auth_ctx = NULL; 324 } 325 if (authctxt->krb5_ctx) { 326 krb5_free_context(authctxt->krb5_ctx); 327 authctxt->krb5_ctx = NULL; 328 } 329} 330 331#endif /* KRB5 */ 332