xref: /openbsd-src/usr.bin/ssh/auth-krb5.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
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  * $OpenBSD: auth-krb5.c,v 1.1 2001/06/26 16:15:23 dugsong Exp $
6  */
7 
8 #include "includes.h"
9 #include "ssh.h"
10 #include "ssh1.h"
11 #include "packet.h"
12 #include "xmalloc.h"
13 #include "log.h"
14 #include "servconf.h"
15 #include "uidswap.h"
16 #include "auth.h"
17 
18 #ifdef KRB5
19 #include <krb5.h>
20 
21 extern ServerOptions	 options;
22 
23 static int
24 krb5_init(void *context)
25 {
26 	Authctxt *authctxt = (Authctxt *)context;
27 	krb5_error_code problem;
28 	static int cleanup_registered = 0;
29 
30 	if (authctxt->krb5_ctx == NULL) {
31 		problem = krb5_init_context(&authctxt->krb5_ctx);
32 		if (problem)
33 			return (problem);
34 		krb5_init_ets(authctxt->krb5_ctx);
35 	}
36 	if (!cleanup_registered) {
37 		fatal_add_cleanup(krb5_cleanup_proc, authctxt);
38 		cleanup_registered = 1;
39 	}
40 	return (0);
41 }
42 
43 /*
44  * Try krb5 authentication. server_user is passed for logging purposes
45  * only, in auth is received ticket, in client is returned principal
46  * from the ticket
47  */
48 int
49 auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client)
50 {
51 	krb5_error_code problem;
52 	krb5_principal server;
53 	krb5_data reply;
54 	krb5_ticket *ticket;
55 	int fd;
56 
57 	server = NULL;
58 	ticket = NULL;
59 	reply.length = 0;
60 
61 	problem = krb5_init(authctxt);
62 	if (problem)
63 		goto err;
64 
65 	problem = krb5_auth_con_init(authctxt->krb5_ctx,
66 	    &authctxt->krb5_auth_ctx);
67 	if (problem)
68 		goto err;
69 
70 	fd = packet_get_connection_in();
71 	problem = krb5_auth_con_setaddrs_from_fd(authctxt->krb5_ctx,
72 	    authctxt->krb5_auth_ctx, &fd);
73 	if (problem)
74 		goto err;
75 
76 	problem = krb5_sname_to_principal(authctxt->krb5_ctx,  NULL, NULL ,
77 	    KRB5_NT_SRV_HST, &server);
78 	if (problem)
79 		goto err;
80 
81 	problem = krb5_rd_req(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx,
82 	    auth, server, NULL, NULL, &ticket);
83 	if (problem)
84 		goto err;
85 
86 	problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->client,
87 	    &authctxt->krb5_user);
88 	if (problem)
89 		goto err;
90 
91 	/* if client wants mutual auth */
92 	problem = krb5_mk_rep(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
93 	    &reply);
94 	if (problem)
95 		goto err;
96 
97 	/* Check .k5login authorization now. */
98 	if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user,
99 	    authctxt->pw->pw_name))
100 		goto err;
101 
102 	if (client)
103 		krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
104 		    client);
105 
106 	packet_start(SSH_SMSG_AUTH_KERBEROS_RESPONSE);
107 	packet_put_string((char *) reply.data, reply.length);
108 	packet_send();
109 	packet_write_wait();
110 
111  err:
112 	if (server)
113 		krb5_free_principal(authctxt->krb5_ctx, server);
114 	if (ticket)
115 		krb5_free_ticket(authctxt->krb5_ctx, ticket);
116 	if (reply.length)
117 		xfree(reply.data);
118 
119 	if (problem) {
120 		debug("Kerberos v5 authentication failed: %s",
121 		    krb5_get_err_text(authctxt->krb5_ctx, problem));
122 		return (0);
123 	}
124 	return (1);
125 }
126 
127 int
128 auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt)
129 {
130 	krb5_error_code problem;
131 	krb5_ccache ccache = NULL;
132 	char *pname;
133 
134 	if (authctxt->pw == NULL || authctxt->krb5_user == NULL)
135 		return (0);
136 
137 	temporarily_use_uid(authctxt->pw);
138 
139 	problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_fcc_ops, &ccache);
140 	if (problem)
141 		goto fail;
142 
143 	problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache,
144 	    authctxt->krb5_user);
145 	if (problem)
146 		goto fail;
147 
148 	problem = krb5_rd_cred2(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
149 	    ccache, tgt);
150 	if (problem)
151 		goto fail;
152 
153 	authctxt->krb5_fwd_ccache = ccache;
154 	ccache = NULL;
155 
156 	authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
157 
158 	problem = krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
159 	    &pname);
160 	if (problem)
161 		goto fail;
162 
163 	debug("Kerberos v5 TGT accepted (%s)", pname);
164 
165 	restore_uid();
166 
167 	return (1);
168 
169  fail:
170 	if (problem)
171 		debug("Kerberos v5 TGT passing failed: %s",
172 		    krb5_get_err_text(authctxt->krb5_ctx, problem));
173 	if (ccache)
174 		krb5_cc_destroy(authctxt->krb5_ctx, ccache);
175 
176 	restore_uid();
177 
178 	return (0);
179 }
180 
181 int
182 auth_krb5_password(Authctxt *authctxt, const char *password)
183 {
184 	krb5_error_code problem;
185 
186 	if (authctxt->pw == NULL)
187 		return (0);
188 
189 	temporarily_use_uid(authctxt->pw);
190 
191 	problem = krb5_init(authctxt);
192 	if (problem)
193 		goto out;
194 
195 	problem = krb5_parse_name(authctxt->krb5_ctx, authctxt->pw->pw_name,
196 		    &authctxt->krb5_user);
197 	if (problem)
198 		goto out;
199 
200 	problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_mcc_ops,
201 	    &authctxt->krb5_fwd_ccache);
202 	if (problem)
203 		goto out;
204 
205 	problem = krb5_cc_initialize(authctxt->krb5_ctx,
206 	    authctxt->krb5_fwd_ccache, authctxt->krb5_user);
207 	if (problem)
208 		goto out;
209 
210 	problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user,
211 	    authctxt->krb5_fwd_ccache, password, 1, NULL);
212 	if (problem)
213 		goto out;
214 
215 	authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
216 
217  out:
218 	restore_uid();
219 
220 	if (problem) {
221 		debug("Kerberos password authentication failed: %s",
222 		    krb5_get_err_text(authctxt->krb5_ctx, problem));
223 
224 		krb5_cleanup_proc(authctxt);
225 
226 		if (options.kerberos_or_local_passwd)
227 			return (-1);
228 		else
229 			return (0);
230 	}
231 	return (1);
232 }
233 
234 void
235 krb5_cleanup_proc(void *context)
236 {
237 	Authctxt *authctxt = (Authctxt *)context;
238 
239 	debug("krb5_cleanup_proc called");
240 	if (authctxt->krb5_fwd_ccache) {
241 		krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
242 		authctxt->krb5_fwd_ccache = NULL;
243 	}
244 	if (authctxt->krb5_user) {
245 		krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user);
246 		authctxt->krb5_user = NULL;
247 	}
248 	if (authctxt->krb5_auth_ctx) {
249 		krb5_auth_con_free(authctxt->krb5_ctx,
250 		    authctxt->krb5_auth_ctx);
251 		authctxt->krb5_auth_ctx = NULL;
252 	}
253 	if (authctxt->krb5_ctx) {
254 		krb5_free_context(authctxt->krb5_ctx);
255 		authctxt->krb5_ctx = NULL;
256 	}
257 }
258 
259 #endif /* KRB5 */
260