]> andersk Git - gssapi-openssh.git/blame - openssh/gss-serv.c
GSSAPI patch for OpenSSH 3.0.2p1 Protocol version 2 by Simon Wilkinson from
[gssapi-openssh.git] / openssh / gss-serv.c
CommitLineData
5598e598 1/*
2 * Copyright (c) 2001 Simon Wilkinson. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
26
27#ifdef GSSAPI
28
29#include "ssh.h"
30#include "ssh2.h"
31#include "xmalloc.h"
32#include "buffer.h"
33#include "bufaux.h"
34#include "packet.h"
35#include "compat.h"
36#include <openssl/evp.h>
37#include "cipher.h"
38#include "kex.h"
39#include "auth.h"
40#include "log.h"
41#include "session.h"
42#include "dispatch.h"
43#include "servconf.h"
44
45#include "ssh-gss.h"
46
47extern ServerOptions options;
48extern u_char *session_id2;
49extern int session_id2_len;
50
51
52typedef struct ssh_gssapi_cred_cache {
53 char *filename;
54 char *envvar;
55 char *envval;
56 void *data;
57} ssh_gssapi_cred_cache;
58
59static struct ssh_gssapi_cred_cache gssapi_cred_store = {NULL,NULL,NULL};
60
61#ifdef KRB5
62
63#ifdef HEIMDAL
64#include <krb5.h>
65#else
66#include <gssapi_krb5.h>
67#define krb5_get_err_text(context,code) error_message(code)
68#endif
69
70static krb5_context krb_context = NULL;
71
72/* Initialise the krb5 library, so we can use it for those bits that
73 * GSSAPI won't do */
74
75int ssh_gssapi_krb5_init() {
76 krb5_error_code problem;
77
78 if (krb_context !=NULL)
79 return 1;
80
81 problem = krb5_init_context(&krb_context);
82 if (problem) {
83 log("Cannot initialize krb5 context");
84 return 0;
85 }
86 krb5_init_ets(krb_context);
87
88 return 1;
89}
90
91/* Check if this user is OK to login. This only works with krb5 - other
92 * GSSAPI mechanisms will need their own.
93 * Returns true if the user is OK to log in, otherwise returns 0
94 */
95
96int
97ssh_gssapi_krb5_userok(char *name) {
98 krb5_principal princ;
99 int retval;
100
101 if (ssh_gssapi_krb5_init() == 0)
102 return 0;
103
104 if ((retval=krb5_parse_name(krb_context, gssapi_client_name.value,
105 &princ))) {
106 log("krb5_parse_name(): %.100s",
107 krb5_get_err_text(krb_context,retval));
108 return 0;
109 }
110 if (krb5_kuserok(krb_context, princ, name)) {
111 retval = 1;
112 log("Authorized to %s, krb5 principal %s (krb5_kuserok)",name,
113 (char *)gssapi_client_name.value);
114 }
115 else
116 retval = 0;
117
118 krb5_free_principal(krb_context, princ);
119 return retval;
120}
121
122/* Make sure that this is called _after_ we've setuid to the user */
123
124/* This writes out any forwarded credentials. Its specific to the Kerberos
125 * GSSAPI mechanism
126 *
127 * We assume that our caller has made sure that the user has selected
128 * delegated credentials, and that the client_creds structure is correctly
129 * populated.
130 */
131
132void
133ssh_gssapi_krb5_storecreds() {
134 krb5_ccache ccache;
135 krb5_error_code problem;
136 krb5_principal princ;
137 char ccname[35];
138 static char name[40];
139 int tmpfd;
140 OM_uint32 maj_status,min_status;
141
142
143 if (gssapi_client_creds==NULL) {
144 debug("No credentials stored");
145 return;
146 }
147
148 if (ssh_gssapi_krb5_init() == 0)
149 return;
150
151 if (options.gss_use_session_ccache) {
152 snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d_XXXXXX",geteuid());
153
154 if ((tmpfd = mkstemp(ccname))==-1) {
155 log("mkstemp(): %.100s", strerror(errno));
156 return;
157 }
158 if (fchmod(tmpfd, S_IRUSR | S_IWUSR) == -1) {
159 log("fchmod(): %.100s", strerror(errno));
160 close(tmpfd);
161 return;
162 }
163 } else {
164 snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d",geteuid());
165 tmpfd = open(ccname, O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
166 if (tmpfd == -1) {
167 log("open(): %.100s", strerror(errno));
168 return;
169 }
170 }
171
172 close(tmpfd);
173 snprintf(name, sizeof(name), "FILE:%s",ccname);
174
175 if ((problem = krb5_cc_resolve(krb_context, name, &ccache))) {
176 log("krb5_cc_default(): %.100s",
177 krb5_get_err_text(krb_context,problem));
178 return;
179 }
180
181 if ((problem = krb5_parse_name(krb_context, gssapi_client_name.value,
182 &princ))) {
183 log("krb5_parse_name(): %.100s",
184 krb5_get_err_text(krb_context,problem));
185 krb5_cc_destroy(krb_context,ccache);
186 return;
187 }
188
189 if ((problem = krb5_cc_initialize(krb_context, ccache, princ))) {
190 log("krb5_cc_initialize(): %.100s",
191 krb5_get_err_text(krb_context,problem));
192 krb5_free_principal(krb_context,princ);
193 krb5_cc_destroy(krb_context,ccache);
194 return;
195 }
196
197 krb5_free_principal(krb_context,princ);
198
199 #ifdef HEIMDAL
200 if ((problem = krb5_cc_copy_cache(krb_context,
201 gssapi_client_creds->ccache,
202 ccache))) {
203 log("krb5_cc_copy_cache(): %.100s",
204 krb5_get_err_text(krb_context,problem));
205 krb5_cc_destroy(krb_context,ccache);
206 return;
207 }
208 #else
209 if ((maj_status = gss_krb5_copy_ccache(&min_status,
210 gssapi_client_creds,
211 ccache))) {
212 log("gss_krb5_copy_ccache() failed");
213 ssh_gssapi_error(maj_status,min_status);
214 krb5_cc_destroy(krb_context,ccache);
215 return;
216 }
217 #endif
218
219 krb5_cc_close(krb_context,ccache);
220
221
222#ifdef USE_PAM
223 do_pam_putenv("KRB5CCNAME",name);
224#endif
225
226 gssapi_cred_store.filename=strdup(ccname);
227 gssapi_cred_store.envvar="KRB5CCNAME";
228 gssapi_cred_store.envval=strdup(name);
229
230 return;
231}
232
233#endif /* KRB5 */
234
235#ifdef GSI
236#include <globus_gss_assist.h>
237
238/*
239 * Check if this user is OK to login under GSI. User has been authenticated
240 * as identity in global 'client_name.value' and is trying to log in as passed
241 * username in 'name'.
242 *
243 * Returns non-zero if user is authorized, 0 otherwise.
244 */
245int
246ssh_gssapi_gsi_userok(char *name)
247{
248 int authorized = 0;
249
250 /* This returns 0 on success */
251 authorized = (globus_gss_assist_userok(gssapi_client_name.value,
252 name) == 0);
253
254 debug("GSI user %s is%s authorized as target user %s",
255 (char *) gssapi_client_name.value,
256 (authorized ? "" : " not"),
257 name);
258
259 return authorized;
260}
261
262/*
263 * Handle setting up child environment for GSI.
264 *
265 * Make sure that this is called _after_ we've setuid to the user.
266 */
267void
268ssh_gssapi_gsi_storecreds()
269{
270 OM_uint32 major_status;
271 OM_uint32 minor_status;
272
273
274 if (gssapi_client_creds != NULL)
275 {
276 char *creds_env = NULL;
277
278 /*
279 * This is the current hack with the GSI gssapi library to
280 * export credentials to disk.
281 */
282
283 debug("Exporting delegated credentials");
284
285 minor_status = 0xdee0; /* Magic value */
286 major_status =
287 gss_inquire_cred(&minor_status,
288 gssapi_client_creds,
289 (gss_name_t *) &creds_env,
290 NULL,
291 NULL,
292 NULL);
293
294 if ((major_status == GSS_S_COMPLETE) &&
295 (minor_status == 0xdee1) &&
296 (creds_env != NULL))
297 {
298 char *value;
299
300 /*
301 * String is of the form:
302 * X509_USER_DELEG_PROXY=filename
303 * so we parse out the filename
304 * and then set X509_USER_PROXY
305 * to point at it.
306 */
307 value = strchr(creds_env, '=');
308
309 if (value != NULL)
310 {
311 *value = '\0';
312 value++;
313#ifdef USE_PAM
314 do_pam_putenv("X509_USER_PROXY",value);
315#endif
316 gssapi_cred_store.filename=NULL;
317 gssapi_cred_store.envvar="X509_USER_PROXY";
318 gssapi_cred_store.envval=strdup(value);
319
320 return;
321 }
322 else
323 {
324 log("Failed to parse delegated credentials string '%s'",
325 creds_env);
326 }
327 }
328 else
329 {
330 log("Failed to export delegated credentials (error %ld)",
331 major_status);
332 }
333 }
334}
335
336#endif /* GSI */
337
338void
339ssh_gssapi_cleanup_creds(void *ignored)
340{
341 if (gssapi_cred_store.filename!=NULL) {
342 /* Unlink probably isn't sufficient */
343 debug("removing gssapi cred file\"%s\"",gssapi_cred_store.filename);
344 unlink(gssapi_cred_store.filename);
345 }
346}
347
348void
349ssh_gssapi_storecreds()
350{
351 switch (gssapi_client_type) {
352#ifdef KRB5
353 case GSS_KERBEROS:
354 ssh_gssapi_krb5_storecreds();
355 break;
356#endif
357#ifdef GSI
358 case GSS_GSI:
359 ssh_gssapi_gsi_storecreds();
360 break;
361#endif /* GSI */
362 case GSS_LAST_ENTRY:
363 /* GSSAPI not used in this authentication */
364 debug("No GSSAPI credentials stored");
365 break;
366 default:
367 log("ssh_gssapi_do_child: Unknown mechanism");
368
369 }
370
371 if (options.gss_cleanup_creds) {
372 fatal_add_cleanup(ssh_gssapi_cleanup_creds, NULL);
373 }
374
375}
376
377/* This allows GSSAPI methods to do things to the childs environment based
378 * on the passed authentication process and credentials.
379 *
380 * Question: If we didn't use userauth_external for some reason, should we
381 * still delegate credentials?
382 */
383void
384ssh_gssapi_do_child(char ***envp, u_int *envsizep)
385{
386
387 if (gssapi_cred_store.envvar!=NULL &&
388 gssapi_cred_store.envval!=NULL) {
389
390 debug("Setting %s to %s", gssapi_cred_store.envvar,
391 gssapi_cred_store.envval);
392 child_set_env(envp, envsizep, gssapi_cred_store.envvar,
393 gssapi_cred_store.envval);
394 }
395
396 switch(gssapi_client_type) {
397#ifdef KRB5
398 case GSS_KERBEROS: break;
399#endif
400#ifdef GSI
401 case GSS_GSI: break;
402#endif
403 case GSS_LAST_ENTRY:
404 debug("No GSSAPI credentials stored");
405
406 default:
407 log("ssh_gssapi_do_child: Unknown mechanism");
408 }
409}
410
411int
412ssh_gssapi_userok(char *user)
413{
414 if (gssapi_client_name.length==0 ||
415 gssapi_client_name.value==NULL) {
416 debug("No suitable client data");
417 return 0;
418 }
419 switch (gssapi_client_type) {
420#ifdef KRB5
421 case GSS_KERBEROS:
422 return(ssh_gssapi_krb5_userok(user));
423 break; /* Not reached */
424#endif
425#ifdef GSI
426 case GSS_GSI:
427 return(ssh_gssapi_gsi_userok(user));
428 break; /* Not reached */
429#endif /* GSI */
430 case GSS_LAST_ENTRY:
431 debug("Client not GSSAPI");
432 break;
433 default:
434 debug("Unknown client authentication type");
435 }
436 return(0);
437}
438
439int
440userauth_external(Authctxt *authctxt)
441{
442 packet_done();
443
444 return(ssh_gssapi_userok(authctxt->user));
445}
446
447void input_gssapi_token(int type, int plen, void *ctxt);
448void input_gssapi_exchange_complete(int type, int plen, void *ctxt);
449
450/* We only support those mechanisms that we know about (ie ones that we know
451 * how to check local user kuserok and the like
452 */
453int
454userauth_gssapi(Authctxt *authctxt)
455{
456 gss_OID_desc oid= {0,NULL};
457 Gssctxt *ctxt;
458 int mechs;
459 gss_OID_set supported;
460 int present;
461 OM_uint32 ms;
462
463 if (!authctxt->valid || authctxt->user == NULL)
464 return 0;
465 mechs=packet_get_int();
466 if (mechs==0) {
467 debug("Mechanism negotiation is not supported");
468 return 0;
469 }
470
471 ssh_gssapi_supported_oids(&supported);
472 do {
473 if (oid.elements)
474 xfree(oid.elements);
475 oid.elements = packet_get_string(&oid.length);
476 gss_test_oid_set_member(&ms, &oid, supported, &present);
477 mechs--;
478 } while (mechs>0 && !present);
479
480 if (!present) {
481 xfree(oid.elements);
482 return(0);
483 }
484
485 ctxt=xmalloc(sizeof(Gssctxt));
486 authctxt->methoddata=(void *)ctxt;
487
488 ssh_gssapi_build_ctx(ctxt);
489 ssh_gssapi_set_oid(ctxt,&oid);
490
491 if (ssh_gssapi_acquire_cred(ctxt))
492 return 0;
493
494 /* Send SSH_MSG_USERAUTH_GSSAPI_RESPONSE */
495
496 packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE);
497 packet_put_string(oid.elements,oid.length);
498 packet_send();
499 packet_write_wait();
500 xfree(oid.elements);
501
502 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,
503 &input_gssapi_token);
504 authctxt->postponed = 1;
505
506 return 0;
507}
508
509void
510input_gssapi_token(int type, int plen, void *ctxt)
511{
512 Authctxt *authctxt = ctxt;
513 Gssctxt *gssctxt;
514 gss_buffer_desc send_tok,recv_tok;
515 OM_uint32 maj_status, min_status;
516
517 if (authctxt == NULL || authctxt->methoddata == NULL)
518 fatal("No authentication or GSSAPI context");
519
520 gssctxt=authctxt->methoddata;
521
522 recv_tok.value=packet_get_string(&recv_tok.length);
523
524 maj_status=ssh_gssapi_accept_ctx(gssctxt, &recv_tok, &send_tok, NULL);
525 packet_done();
526
527 if (GSS_ERROR(maj_status)) {
528 /* Failure <sniff> */
529 authctxt->postponed = 0;
530 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
531 userauth_finish(authctxt, 0, "gssapi");
532 }
533
534 if (send_tok.length != 0) {
535 /* Send a packet back to the client */
536 packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
537 packet_put_string(send_tok.value,send_tok.length);
538 packet_send();
539 packet_write_wait();
540 gss_release_buffer(&min_status, &send_tok);
541 }
542
543 if (maj_status == GSS_S_COMPLETE) {
544 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,NULL);
545 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,
546 &input_gssapi_exchange_complete);
547 }
548}
549
550/* This is called when the client thinks we've completed authentication.
551 * It should only be enabled in the dispatch handler by the function above,
552 * which only enables it once the GSSAPI exchange is complete.
553 */
554
555void
556input_gssapi_exchange_complete(int type, int plen, void *ctxt)
557{
558 Authctxt *authctxt = ctxt;
559 Gssctxt *gssctxt;
560 int authenticated;
561
562 if (authctxt == NULL || authctxt->methoddata == NULL)
563 fatal("No authentication or GSSAPI context");
564
565 gssctxt=authctxt->methoddata;
566
567 /* This should never happen, but better safe than sorry. */
568 if (gssctxt->status != GSS_S_COMPLETE) {
569 packet_disconnect("Context negotiation is not complete");
570 }
571
572 if (ssh_gssapi_getclient(gssctxt,&gssapi_client_type,
573 &gssapi_client_name,
574 &gssapi_client_creds)) {
575 fatal("Couldn't convert client name");
576 }
577
578 authenticated = ssh_gssapi_userok(authctxt->user);
579
580 authctxt->postponed = 0;
581 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
582 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
583 userauth_finish(authctxt, authenticated, "gssapi");
584}
585
586#endif /* GSSAPI */
This page took 0.720348 seconds and 5 git commands to generate.