1 /* $NetBSD: auth-krb5.c,v 1.20 2026/04/08 18:58:40 christos Exp $ */ 2 /* $OpenBSD: auth-krb5.c,v 1.26 2026/02/08 19:54:31 dtucker 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.20 2026/04/08 18:58:40 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 "misc.h" 49 #include "servconf.h" 50 #include "uidswap.h" 51 #include "hostfile.h" 52 #include "auth.h" 53 54 #ifdef KRB5 55 #include <errno.h> 56 #include <unistd.h> 57 #include <string.h> 58 #include <krb5.h> 59 60 extern ServerOptions options; 61 62 static int 63 krb5_init(void *context) 64 { 65 Authctxt *authctxt = (Authctxt *)context; 66 krb5_error_code problem; 67 68 if (authctxt->krb5_ctx == NULL) { 69 problem = krb5_init_context(&authctxt->krb5_ctx); 70 if (problem) 71 return (problem); 72 krb5_init_ets(authctxt->krb5_ctx); 73 } 74 return (0); 75 } 76 77 /* 78 * Try krb5 authentication. server_user is passed for logging purposes 79 * only, in auth is received ticket, in client is returned principal 80 * from the ticket 81 */ 82 int 83 auth_krb5(struct ssh *ssh, krb5_data *auth, char **client, krb5_data *reply) 84 { 85 krb5_error_code problem; 86 krb5_principal server; 87 krb5_ticket *ticket; 88 int fd, ret; 89 const char *errtxt; 90 Authctxt *authctxt = ssh->authctxt; 91 92 ret = 0; 93 server = NULL; 94 ticket = NULL; 95 reply->length = 0; 96 97 problem = krb5_init(authctxt); 98 if (problem) 99 goto err; 100 101 problem = krb5_auth_con_init(authctxt->krb5_ctx, 102 &authctxt->krb5_auth_ctx); 103 if (problem) 104 goto err; 105 106 fd = ssh_packet_get_connection_in(ssh); 107 problem = krb5_auth_con_setaddrs_from_fd(authctxt->krb5_ctx, 108 authctxt->krb5_auth_ctx, &fd); 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 problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->client, 123 &authctxt->krb5_user); 124 if (problem) 125 goto err; 126 127 /* if client wants mutual auth */ 128 problem = krb5_mk_rep(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, 129 reply); 130 if (problem) 131 goto err; 132 133 /* Check .k5login authorization now. */ 134 if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, 135 authctxt->pw->pw_name)) 136 goto err; 137 138 if (client) 139 krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user, 140 client); 141 142 ret = 1; 143 err: 144 if (server) 145 krb5_free_principal(authctxt->krb5_ctx, server); 146 if (ticket) 147 krb5_free_ticket(authctxt->krb5_ctx, ticket); 148 if (!ret && reply->length) { 149 free(reply->data); 150 memset(reply, 0, sizeof(*reply)); 151 } 152 153 if (problem) { 154 errtxt = NULL; 155 if (authctxt->krb5_ctx != NULL) 156 errtxt = krb5_get_error_message(authctxt->krb5_ctx, 157 problem); 158 if (errtxt != NULL) { 159 debug("Kerberos v5 authentication failed: %s", errtxt); 160 krb5_free_error_message(authctxt->krb5_ctx, errtxt); 161 } else 162 debug("Kerberos v5 authentication failed: %d", 163 problem); 164 } 165 166 return (ret); 167 } 168 169 int 170 auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt) 171 { 172 krb5_error_code problem; 173 krb5_ccache ccache = NULL; 174 char *pname; 175 const char *errtxt; 176 177 if (authctxt->pw == NULL || authctxt->krb5_user == NULL) 178 return (0); 179 180 temporarily_use_uid(authctxt->pw); 181 182 problem = krb5_cc_new_unique(authctxt->krb5_ctx, "FILE", NULL, &ccache); 183 if (problem) 184 goto fail; 185 186 problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache, 187 authctxt->krb5_user); 188 if (problem) 189 goto fail; 190 191 problem = krb5_rd_cred2(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, 192 ccache, tgt); 193 if (problem) 194 goto fail; 195 196 authctxt->krb5_fwd_ccache = ccache; 197 ccache = NULL; 198 199 authctxt->krb5_ticket_file = __UNCONST(krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache)); 200 201 problem = krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user, 202 &pname); 203 if (problem) 204 goto fail; 205 206 #ifdef USE_PAM 207 if (options.use_pam) 208 do_pam_putenv(__UNCONST("KRB5CCNAME"), authctxt->krb5_ticket_file); 209 #endif 210 debug("Kerberos v5 TGT accepted (%s)", pname); 211 212 restore_uid(); 213 214 return (1); 215 216 fail: 217 if (problem) { 218 errtxt = krb5_get_error_message(authctxt->krb5_ctx, problem); 219 if (errtxt != NULL) { 220 debug("Kerberos v5 TGT passing failed: %s", errtxt); 221 krb5_free_error_message(authctxt->krb5_ctx, errtxt); 222 } else 223 debug("Kerberos v5 TGT passing failed: %d", problem); 224 } 225 if (ccache) 226 krb5_cc_destroy(authctxt->krb5_ctx, ccache); 227 228 restore_uid(); 229 230 return (0); 231 } 232 233 234 int 235 auth_krb5_password(Authctxt *authctxt, const char *password) 236 { 237 krb5_error_code problem; 238 krb5_ccache ccache = NULL; 239 const char *errmsg; 240 241 temporarily_use_uid(authctxt->pw); 242 243 problem = krb5_init(authctxt); 244 if (problem) 245 goto out; 246 247 problem = krb5_parse_name(authctxt->krb5_ctx, authctxt->pw->pw_name, 248 &authctxt->krb5_user); 249 if (problem) 250 goto out; 251 252 problem = krb5_cc_new_unique(authctxt->krb5_ctx, 253 krb5_mcc_ops.prefix, NULL, &ccache); 254 if (problem) 255 goto out; 256 257 problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache, 258 authctxt->krb5_user); 259 if (problem) 260 goto out; 261 262 restore_uid(); 263 264 problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user, 265 ccache, password, 1, NULL); 266 267 temporarily_use_uid(authctxt->pw); 268 269 if (problem) 270 goto out; 271 272 problem = krb5_cc_new_unique(authctxt->krb5_ctx, 273 krb5_fcc_ops.prefix, NULL, &authctxt->krb5_fwd_ccache); 274 if (problem) 275 goto out; 276 277 problem = krb5_cc_copy_cache(authctxt->krb5_ctx, ccache, 278 authctxt->krb5_fwd_ccache); 279 krb5_cc_destroy(authctxt->krb5_ctx, ccache); 280 ccache = NULL; 281 if (problem) 282 goto out; 283 284 authctxt->krb5_ticket_file = __UNCONST(krb5_cc_get_name( 285 authctxt->krb5_ctx, authctxt->krb5_fwd_ccache)); 286 287 out: 288 restore_uid(); 289 290 if (problem) { 291 if (ccache) 292 krb5_cc_destroy(authctxt->krb5_ctx, ccache); 293 294 if (authctxt->krb5_ctx != NULL) { 295 errmsg = krb5_get_error_message(authctxt->krb5_ctx, 296 problem); 297 debug("Kerberos password authentication failed: %s", 298 errmsg); 299 krb5_free_error_message(authctxt->krb5_ctx, errmsg); 300 } else 301 debug("Kerberos password authentication failed: %d", 302 problem); 303 304 krb5_cleanup_proc(authctxt); 305 306 if (options.kerberos_or_local_passwd) 307 return (-1); 308 else 309 return (0); 310 } 311 return (authctxt->valid ? 1 : 0); 312 } 313 314 void 315 krb5_cleanup_proc(Authctxt *authctxt) 316 { 317 debug("krb5_cleanup_proc called"); 318 if (authctxt->krb5_fwd_ccache) { 319 krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); 320 authctxt->krb5_fwd_ccache = NULL; 321 } 322 if (authctxt->krb5_user) { 323 krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user); 324 authctxt->krb5_user = NULL; 325 } 326 if (authctxt->krb5_auth_ctx) { 327 krb5_auth_con_free(authctxt->krb5_ctx, 328 authctxt->krb5_auth_ctx); 329 authctxt->krb5_auth_ctx = NULL; 330 } 331 if (authctxt->krb5_ctx) { 332 krb5_free_context(authctxt->krb5_ctx); 333 authctxt->krb5_ctx = NULL; 334 } 335 } 336 337 #endif /* KRB5 */ 338