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