Home | History | Annotate | Line # | Download | only in dist
      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