Home | History | Annotate | Download | only in sshd
      1 /*
      2  *    Kerberos v5 authentication and ticket-passing routines.
      3  *
      4  * $FreeBSD: 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.9 2002/09/09 06:48:06 itojun Exp $");
     32 
     33 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     34 
     35 #include "ssh.h"
     36 #include "ssh1.h"
     37 #include "packet.h"
     38 #include "xmalloc.h"
     39 #include "log.h"
     40 #include "servconf.h"
     41 #include "uidswap.h"
     42 #include "auth.h"
     43 
     44 #ifdef KRB5
     45 #include <krb5.h>
     46 #ifndef HEIMDAL
     47 #define krb5_get_err_text(context,code) error_message(code)
     48 #endif /* !HEIMDAL */
     49 
     50 extern ServerOptions	 options;
     51 
     52 static int
     53 krb5_init(void *context)
     54 {
     55 	Authctxt *authctxt = (Authctxt *)context;
     56 	krb5_error_code problem;
     57 	static int cleanup_registered = 0;
     58 
     59 	if (authctxt->krb5_ctx == NULL) {
     60 		problem = krb5_init_context(&authctxt->krb5_ctx);
     61 		if (problem)
     62 			return (problem);
     63 		krb5_init_ets(authctxt->krb5_ctx);
     64 	}
     65 	if (!cleanup_registered) {
     66 		fatal_add_cleanup(krb5_cleanup_proc, authctxt);
     67 		cleanup_registered = 1;
     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  */
     77 int
     78 auth_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 
     85 	ret = 0;
     86 	server = NULL;
     87 	ticket = NULL;
     88 	reply->length = 0;
     89 
     90 	problem = krb5_init(authctxt);
     91 	if (problem)
     92 		goto err;
     93 
     94 	problem = krb5_auth_con_init(authctxt->krb5_ctx,
     95 	    &authctxt->krb5_auth_ctx);
     96 	if (problem)
     97 		goto err;
     98 
     99 	fd = packet_get_connection_in();
    100 #ifdef HEIMDAL
    101 	problem = krb5_auth_con_setaddrs_from_fd(authctxt->krb5_ctx,
    102 	    authctxt->krb5_auth_ctx, &fd);
    103 #else
    104 	problem = krb5_auth_con_genaddrs(authctxt->krb5_ctx,
    105 	    authctxt->krb5_auth_ctx,fd,
    106 	    KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR |
    107 	    KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR);
    108 #endif
    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 #ifdef HEIMDAL
    123 	problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->client,
    124 	    &authctxt->krb5_user);
    125 #else
    126 	problem = krb5_copy_principal(authctxt->krb5_ctx,
    127 				      ticket->enc_part2->client,
    128 				      &authctxt->krb5_user);
    129 #endif
    130 	if (problem)
    131 		goto err;
    132 
    133 	/* if client wants mutual auth */
    134 	problem = krb5_mk_rep(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
    135 	    reply);
    136 	if (problem)
    137 		goto err;
    138 
    139 	/* Check .k5login authorization now. */
    140 	if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user,
    141 	    authctxt->pw->pw_name))
    142 		goto err;
    143 
    144 	if (client)
    145 		krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
    146 		    client);
    147 
    148 	ret = 1;
    149  err:
    150 	if (server)
    151 		krb5_free_principal(authctxt->krb5_ctx, server);
    152 	if (ticket)
    153 		krb5_free_ticket(authctxt->krb5_ctx, ticket);
    154 	if (!ret && reply->length) {
    155 		xfree(reply->data);
    156 		memset(reply, 0, sizeof(*reply));
    157 	}
    158 
    159 	if (problem) {
    160 		if (authctxt->krb5_ctx != NULL)
    161 			debug("Kerberos v5 authentication failed: %s",
    162 			    krb5_get_err_text(authctxt->krb5_ctx, problem));
    163 		else
    164 			debug("Kerberos v5 authentication failed: %d",
    165 			    problem);
    166 	}
    167 
    168 	return (ret);
    169 }
    170 
    171 int
    172 auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt)
    173 {
    174 	krb5_error_code problem;
    175 	krb5_ccache ccache = NULL;
    176 	char *pname;
    177 	krb5_creds **creds;
    178 
    179 	if (authctxt->pw == NULL || authctxt->krb5_user == NULL)
    180 		return (0);
    181 
    182 	temporarily_use_uid(authctxt->pw);
    183 
    184 #ifdef HEIMDAL
    185 	problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_fcc_ops, &ccache);
    186 #else
    187 {
    188 	char ccname[40];
    189 	int tmpfd;
    190 
    191 	snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid());
    192 
    193 	if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) {
    194 		log("mkstemp(): %.100s", strerror(errno));
    195 		problem = errno;
    196 		goto fail;
    197 	}
    198 	if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) {
    199 		log("fchmod(): %.100s", strerror(errno));
    200 		close(tmpfd);
    201 		problem = errno;
    202 		goto fail;
    203 	}
    204 	close(tmpfd);
    205 	problem = krb5_cc_resolve(authctxt->krb5_ctx, ccname, &ccache);
    206 }
    207 #endif
    208 	if (problem)
    209 		goto fail;
    210 
    211 	problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache,
    212 	    authctxt->krb5_user);
    213 	if (problem)
    214 		goto fail;
    215 
    216 #ifdef HEIMDAL
    217 	problem = krb5_rd_cred2(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
    218 	    ccache, tgt);
    219 	if (problem)
    220 		goto fail;
    221 #else
    222 	problem = krb5_rd_cred(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
    223 	    tgt, &creds, NULL);
    224 	if (problem)
    225 		goto fail;
    226 	problem = krb5_cc_store_cred(authctxt->krb5_ctx, ccache, *creds);
    227 	if (problem)
    228 		goto fail;
    229 #endif
    230 
    231 	authctxt->krb5_fwd_ccache = ccache;
    232 	ccache = NULL;
    233 
    234 	authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
    235 
    236 	problem = krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
    237 	    &pname);
    238 	if (problem)
    239 		goto fail;
    240 
    241 	debug("Kerberos v5 TGT accepted (%s)", pname);
    242 
    243 	restore_uid();
    244 
    245 	return (1);
    246 
    247  fail:
    248 	if (problem)
    249 		debug("Kerberos v5 TGT passing failed: %s",
    250 		    krb5_get_err_text(authctxt->krb5_ctx, problem));
    251 	if (ccache)
    252 		krb5_cc_destroy(authctxt->krb5_ctx, ccache);
    253 
    254 	restore_uid();
    255 
    256 	return (0);
    257 }
    258 
    259 int
    260 auth_krb5_password(Authctxt *authctxt, const char *password)
    261 {
    262 #ifndef HEIMDAL
    263 	krb5_creds creds;
    264 	krb5_principal server;
    265 	char ccname[40];
    266 	int tmpfd;
    267 #endif
    268 	krb5_error_code problem;
    269 
    270 	if (authctxt->pw == NULL)
    271 		return (0);
    272 
    273 	temporarily_use_uid(authctxt->pw);
    274 
    275 	problem = krb5_init(authctxt);
    276 	if (problem)
    277 		goto out;
    278 
    279 	problem = krb5_parse_name(authctxt->krb5_ctx, authctxt->pw->pw_name,
    280 		    &authctxt->krb5_user);
    281 	if (problem)
    282 		goto out;
    283 
    284 #ifdef HEIMDAL
    285 	problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_mcc_ops,
    286 	    &authctxt->krb5_fwd_ccache);
    287 	if (problem)
    288 		goto out;
    289 
    290 	problem = krb5_cc_initialize(authctxt->krb5_ctx,
    291 	    authctxt->krb5_fwd_ccache, authctxt->krb5_user);
    292 	if (problem)
    293 		goto out;
    294 
    295 	restore_uid();
    296 	problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user,
    297 	    authctxt->krb5_fwd_ccache, password, 1, NULL);
    298 	temporarily_use_uid(authctxt->pw);
    299 
    300 	if (problem)
    301 		goto out;
    302 
    303 #else
    304 	problem = krb5_get_init_creds_password(authctxt->krb5_ctx, &creds,
    305 	    authctxt->krb5_user, (char *)password, NULL, NULL, 0, NULL, NULL);
    306 	if (problem)
    307 		goto out;
    308 
    309 	problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL,
    310 	    KRB5_NT_SRV_HST, &server);
    311 	if (problem)
    312 		goto out;
    313 
    314 	restore_uid();
    315 	problem = krb5_verify_init_creds(authctxt->krb5_ctx, &creds, server,
    316 	    NULL, NULL, NULL);
    317 	krb5_free_principal(authctxt->krb5_ctx, server);
    318 	temporarily_use_uid(authctxt->pw);
    319 	if (problem)
    320 		goto out;
    321 
    322 	if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user,
    323 			  authctxt->pw->pw_name)) {
    324 		problem = -1;
    325 		goto out;
    326 	}
    327 
    328 	snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid());
    329 
    330 	if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) {
    331 		log("mkstemp(): %.100s", strerror(errno));
    332 		problem = errno;
    333 		goto out;
    334 	}
    335 
    336 	if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) {
    337 		log("fchmod(): %.100s", strerror(errno));
    338 		close(tmpfd);
    339 		problem = errno;
    340 		goto out;
    341 	}
    342 	close(tmpfd);
    343 
    344 	problem = krb5_cc_resolve(authctxt->krb5_ctx, ccname, &authctxt->krb5_fwd_ccache);
    345 	if (problem)
    346 		goto out;
    347 
    348 	problem = krb5_cc_initialize(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache,
    349 				     authctxt->krb5_user);
    350 	if (problem)
    351 		goto out;
    352 
    353 	problem= krb5_cc_store_cred(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache,
    354 				 &creds);
    355 	if (problem)
    356 		goto out;
    357 #endif
    358 
    359 	authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
    360 
    361  out:
    362 	restore_uid();
    363 
    364 	if (problem) {
    365 		if (authctxt->krb5_ctx != NULL && problem!=-1)
    366 			debug("Kerberos password authentication failed: %s",
    367 			    krb5_get_err_text(authctxt->krb5_ctx, problem));
    368 		else
    369 			debug("Kerberos password authentication failed: %d",
    370 			    problem);
    371 
    372 		krb5_cleanup_proc(authctxt);
    373 
    374 		if (options.kerberos_or_local_passwd)
    375 			return (-1);
    376 		else
    377 			return (0);
    378 	}
    379 	return (1);
    380 }
    381 
    382 void
    383 krb5_cleanup_proc(void *context)
    384 {
    385 	Authctxt *authctxt = (Authctxt *)context;
    386 
    387 	debug("krb5_cleanup_proc called");
    388 	if (authctxt->krb5_fwd_ccache) {
    389 		krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
    390 		authctxt->krb5_fwd_ccache = NULL;
    391 	}
    392 	if (authctxt->krb5_user) {
    393 		krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user);
    394 		authctxt->krb5_user = NULL;
    395 	}
    396 	if (authctxt->krb5_auth_ctx) {
    397 		krb5_auth_con_free(authctxt->krb5_ctx,
    398 		    authctxt->krb5_auth_ctx);
    399 		authctxt->krb5_auth_ctx = NULL;
    400 	}
    401 	if (authctxt->krb5_ctx) {
    402 		krb5_free_context(authctxt->krb5_ctx);
    403 		authctxt->krb5_ctx = NULL;
    404 	}
    405 }
    406 
    407 #endif /* KRB5 */
    408