client_userauth.c 23.5 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
2
3
4
5
6
/* client_userauth.c
 *
 */

/* lsh, an implementation of the ssh protocol
 *
7
 * Copyright (C) 1999 Balzs Scheidler, Niels Mller
Niels Möller's avatar
Niels Möller committed
8
9
10
11
12
13
14
15
16
17
18
19
20
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
J.H.M. Dassen's avatar
J.H.M. Dassen committed
21
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Niels Möller's avatar
Niels Möller committed
22
23
 */

Niels Möller's avatar
Niels Möller committed
24
#include "client_userauth.h"
Niels Möller's avatar
Niels Möller committed
25
26

#include "charset.h"
27
#include "command.h"
Niels Möller's avatar
Niels Möller committed
28
29
#include "format.h"
#include "parse.h"
30
#include "publickey_crypto.h"
Niels Möller's avatar
Niels Möller committed
31
32
33
#include "ssh.h"
#include "werror.h"
#include "xalloc.h"
Niels Möller's avatar
Niels Möller committed
34

35
36
#include <assert.h>

Balázs Scheidler's avatar
Balázs Scheidler committed
37
38
/* Forward declaration */
struct client_userauth; 
39

40
41
#include "client_userauth.c.x"

42
43
44
45
46
47
48
49
50
51
52
static struct lsh_string *
format_userauth_none(struct lsh_string *name,
                     int service)
{
  return ssh_format("%c%S%a%a",
		    SSH_MSG_USERAUTH_REQUEST,
		    name,
		    service,
		    ATOM_NONE);
}

53
/* The last parameters says whether or not to free the password. */
Niels Möller's avatar
Niels Möller committed
54
55
56
57
58
struct lsh_string *
format_userauth_password(struct lsh_string *name,
			 int service,
			 struct lsh_string *passwd,
			 int free)
Niels Möller's avatar
Niels Möller committed
59
60
61
62
63
64
65
66
67
68
{
  return ssh_format(free ? "%c%S%a%a%c%fS" : "%c%S%a%a%c%S",
		    SSH_MSG_USERAUTH_REQUEST,
		    name,
		    service,
		    ATOM_PASSWORD,
		    0,
		    passwd);
}

69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
static struct lsh_string *
format_userauth_publickey_query(struct lsh_string *name,
				UINT32 service,
				UINT32 keytype,
				struct lsh_string *public)
{
  return ssh_format("%c%S%a%a%c%a%S",
		    SSH_MSG_USERAUTH_REQUEST,
		    name,
		    service,
		    ATOM_PUBLICKEY,
		    0,
		    keytype,
		    public);
}
Balázs Scheidler's avatar
Balázs Scheidler committed
84

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
static struct lsh_string *
format_userauth_publickey(struct lsh_string *name,
			  UINT32 service,
			  UINT32 keytype,
			  struct lsh_string *public)
{
  return ssh_format("%c%S%a%a%c%a%S",
		    SSH_MSG_USERAUTH_REQUEST,
		    name,
		    service,
		    ATOM_PUBLICKEY,
		    1,
		    keytype,
		    public);
}

/* Called when we receive a USERAUTH_FAILURE message. It will
 * typically either try again, or raise EXC_USERAUTH. */
Balázs Scheidler's avatar
Balázs Scheidler committed
103

104
105
106
107
/* GABA:
   (class
     (name client_userauth_failure)
     (vars
108
109
110
       ; again is true if the server says that
       ; the current method is still useful.
       (failure method void "int again")))
111
*/
Balázs Scheidler's avatar
Balázs Scheidler committed
112

113
#define CLIENT_USERAUTH_FAILURE(f, c) ((f)->failure((f), (c)))
Balázs Scheidler's avatar
Balázs Scheidler committed
114

115
/* Almost like a command, but returns a failure handler. */
Balázs Scheidler's avatar
Balázs Scheidler committed
116
117
/* GABA:
   (class
118
     (name client_userauth_method)
Balázs Scheidler's avatar
Balázs Scheidler committed
119
     (vars
120
       (type . int)
121
       (login method (object client_userauth_failure)
122
123
124
                     "struct client_userauth *u"
                     "struct ssh_connection *c"
		     "struct exception_handler *e")))
Balázs Scheidler's avatar
Balázs Scheidler committed
125
126
*/

127
128
#define CLIENT_USERAUTH_LOGIN(m, u, c, e) \
  ((m)->login((m), (u), (c), (e)))
Niels Möller's avatar
Niels Möller committed
129

130
131
132
133
134
/* Takes a connection as argument, and attempts to login. It does this
 * by trying each METHOD in turn. As soon as one succeeds, the
 * connection is returned. When a method fails (raising a EXC_USERAUTH
 * exception), we try the next method. If all methods fail, we raise
 * EXC_USERAUTH. */
Balázs Scheidler's avatar
Balázs Scheidler committed
135
136
137

/* GABA:
   (class
138
139
140
     (name client_userauth)
     (super command)
     (vars
141
       ; The name should already be converted to utf8
142
       (username string)            ; Remote user name to authenticate as.
143
       (service_name . int)         ; Service we want to access.
144
145
146
147
148
149
150
151
152
153
154
       (methods object object_list) ; Authentication methods, in order.
       ))
*/

/* GABA:
   (class
     (name client_userauth_state)
     (vars
       (userauth object client_userauth)
       (connection object ssh_connection)
       (failure object client_userauth_failure)
155
156
157
158
159
160

       ; Current method
       (current . unsigned)

       ; The next method that the server has indicated is useful.
       (next . unsigned)))
161
162
163
164
165
166
167
168
*/

static struct client_userauth_state *
make_client_userauth_state(struct client_userauth *userauth,
			   struct ssh_connection *connection)
{
  NEW(client_userauth_state, self);

169
  trace("client_userauth.c: make_client_userauth_state\n");
170
171
172
173
  self->connection = connection;
  self->userauth = userauth;
  self->failure = NULL;
  self->current = 0;
174
175
  self->next = 1;
  
176
177
178
179
180
181
  return self;
}

/* GABA:
   (class
     (name userauth_packet_handler)
Balázs Scheidler's avatar
Balázs Scheidler committed
182
183
     (super packet_handler)
     (vars
184
       (state object client_userauth_state)))
Balázs Scheidler's avatar
Balázs Scheidler committed
185
186
*/

187

Balázs Scheidler's avatar
Balázs Scheidler committed
188
189
/* GABA:
   (class
190
     (name userauth_success_handler)
Balázs Scheidler's avatar
Balázs Scheidler committed
191
192
     (super packet_handler)
     (vars
193
       (c object command_continuation)))
Balázs Scheidler's avatar
Balázs Scheidler committed
194
195
*/

Niels Möller's avatar
Niels Möller committed
196
197
198
199
static void
do_userauth_success(struct packet_handler *c,
		    struct ssh_connection *connection,
		    struct lsh_string *packet)
Niels Möller's avatar
Niels Möller committed
200
{
201
  CAST(userauth_success_handler, self, c);
Niels Möller's avatar
Niels Möller committed
202
203
  struct simple_buffer buffer;

Niels Möller's avatar
Niels Möller committed
204
  unsigned msg_number;
205
206
207

  trace("client_userauth.c: do_userauth_success\n");
  
Niels Möller's avatar
Niels Möller committed
208
209
210
211
212
213
  simple_buffer_init(&buffer, packet->length, packet->data);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_USERAUTH_SUCCESS)
      && parse_eod(&buffer))
    {
214
      unsigned i;
Niels Möller's avatar
Niels Möller committed
215
      
216
      verbose("User authentication successful.\n");
217
218

      for (i = SSH_FIRST_USERAUTH_GENERIC; i < SSH_FIRST_CONNECTION_GENERIC; i++) 
219
	connection->dispatch[i] = &connection_fail_handler;
Niels Möller's avatar
Niels Möller committed
220
      
221
      COMMAND_RETURN(self->c, connection);
Niels Möller's avatar
Niels Möller committed
222
223
    }
  else
224
    PROTOCOL_ERROR(connection->e, "Invalid USERAUTH_SUCCESS message");
Niels Möller's avatar
Niels Möller committed
225
226
}

227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
static struct packet_handler *
make_success_handler(struct command_continuation *c)
{
  NEW(userauth_success_handler, self);

  self->super.handler = do_userauth_success;
  self->c = c;

  return &self->super;
}


/* GABA:
   (class
     (name failure_handler)
     (super userauth_packet_handler)
     (vars
       (e object exception_handler)))
*/


248
249
250
/* Arbitrary limit on list length */
#define USERAUTH_MAX_METHODS 47

251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
static int
userauth_method_is_useful(struct client_userauth *userauth,
			  struct int_list *advertised,
			  unsigned n)
{
  CAST_SUBTYPE(client_userauth_method, method,
	       LIST(userauth->methods)[n]);

  unsigned i;

  for(i = 0; i < LIST_LENGTH(advertised); i++)
    if (LIST(advertised)[i] == method->type)
      return 1;

  return 0;
}

Niels Möller's avatar
Niels Möller committed
268
269
270
271
static void
do_userauth_failure(struct packet_handler *c,
		    struct ssh_connection *connection,
		    struct lsh_string *packet)
Niels Möller's avatar
Niels Möller committed
272
{
273
  CAST(userauth_packet_handler, self, c);
Niels Möller's avatar
Niels Möller committed
274
275
  struct simple_buffer buffer;

Niels Möller's avatar
Niels Möller committed
276
  unsigned msg_number;
277
  struct int_list *methods = NULL;
Niels Möller's avatar
Niels Möller committed
278
  int partial_success;
279
280

  trace("client_userauth.c: do_userauth_failure\n");
281
  assert(self->state->current < self->state->next);
282
  
Niels Möller's avatar
Niels Möller committed
283
284
285
286
  simple_buffer_init(&buffer, packet->length, packet->data);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_USERAUTH_FAILURE)
287
      && ( (methods = parse_atom_list(&buffer, USERAUTH_MAX_METHODS)) )
Niels Möller's avatar
Niels Möller committed
288
      && parse_boolean(&buffer, &partial_success)
Niels Möller's avatar
Niels Möller committed
289
290
291
      && parse_eod(&buffer))
    {
      if (partial_success)
Niels Möller's avatar
Niels Möller committed
292
293
294
	/* Doesn't help us */
	werror("Received SSH_MSH_USERAUTH_FAILURE "
	       "indicating partial success.\n");
295

296
297
298
299
300
301
302
303
      while ( (self->state->next < LIST_LENGTH(self->state->userauth->methods))
	      && !userauth_method_is_useful(self->state->userauth,
					    methods, self->state->next))
	self->state->next++;

      CLIENT_USERAUTH_FAILURE(self->state->failure,
			      userauth_method_is_useful(self->state->userauth,
							methods, self->state->current));
Niels Möller's avatar
Niels Möller committed
304
305
    }
  else
306
    PROTOCOL_ERROR(connection->e, "Invalid USERAUTH_FAILURE message.");
307
308
309
310
311
312
313
314
315
  
  KILL(methods);	
}

static struct packet_handler *
make_failure_handler(struct client_userauth_state *state)
{
  NEW(userauth_packet_handler, self);

316
317
  trace("client_userauth.c: make_failure_handler\n");

318
319
320
321
  self->super.handler = do_userauth_failure;
  self->state = state;

  return &self->super;
Niels Möller's avatar
Niels Möller committed
322
323
}

Niels Möller's avatar
Niels Möller committed
324
static void
Niels Möller's avatar
Niels Möller committed
325
do_userauth_banner(struct packet_handler *self UNUSED,
Niels Möller's avatar
Niels Möller committed
326
327
		   struct ssh_connection *connection UNUSED,
		   struct lsh_string *packet)
Niels Möller's avatar
Niels Möller committed
328
329
330
{
  struct simple_buffer buffer;

Niels Möller's avatar
Niels Möller committed
331
  unsigned msg_number;
Niels Möller's avatar
Niels Möller committed
332
  UINT32 length;
Niels Möller's avatar
Niels Möller committed
333
  const UINT8 *msg;
Niels Möller's avatar
Niels Möller committed
334
335

  UINT32 language_length;
Niels Möller's avatar
Niels Möller committed
336
  const UINT8 *language;
Niels Möller's avatar
Niels Möller committed
337
338
339
340
341
342
343
344
345
346
  
  simple_buffer_init(&buffer, packet->length, packet->data);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_USERAUTH_BANNER)
      && parse_string(&buffer, &length, &msg)
      && parse_string(&buffer, &language_length, &language)
      && parse_eod(&buffer))
    {
      /* Ignore language tag */
Niels Möller's avatar
Niels Möller committed
347
      werror("%ups", length, msg);
Niels Möller's avatar
Niels Möller committed
348
    }
Niels Möller's avatar
Niels Möller committed
349
  else
350
    PROTOCOL_ERROR(connection->e, "Invalid USERAUTH_SUCCESS message");
Niels Möller's avatar
Niels Möller committed
351
352
}

353
354
355
static struct packet_handler userauth_banner_handler =
{ STATIC_HEADER, do_userauth_banner };

Niels Möller's avatar
Niels Möller committed
356

357
358
/* GABA:
   (class
359
     (name exc_client_userauth)
360
361
362
363
364
     (super exception_handler)
     (vars
       (state object client_userauth_state)))
*/

365
366
367
/* Skip to the next useful userauth method, or reraise the exception
 * if there are no more methods to try. */

368
static void
369
do_exc_client_userauth(struct exception_handler *s,
370
		       const struct exception *e)
Niels Möller's avatar
Niels Möller committed
371
{
372
  CAST(exc_client_userauth, self, s);
Niels Möller's avatar
Niels Möller committed
373

374
375
376
377
378
  trace("client_userauth.c: do_exc_client_userauth\n");
  assert(self->state->current < self->state->next);

  if ( (e->type == EXC_USERAUTH)
       && (self->state->next < LIST_LENGTH(self->state->userauth->methods)))
379
    {
380
381
382
383
384
385
386
387
388
      self->state->current = self->state->next++;
      {      
	CAST_SUBTYPE(client_userauth_method, method,
		     LIST(self->state->userauth->methods)[self->state->current]);
	
	self->state->failure
	  = CLIENT_USERAUTH_LOGIN(method, self->state->userauth,
				  self->state->connection, s);
      }
389
390
391
    }
  else
    EXCEPTION_RAISE(s->parent, e);
Niels Möller's avatar
Niels Möller committed
392
393
}

394
static struct exception_handler *
395
make_exc_client_userauth(struct client_userauth_state *state,
396
397
			 struct exception_handler *parent,
			 const char *context)
Niels Möller's avatar
Niels Möller committed
398
{
399
  NEW(exc_client_userauth, self);
400

401
  trace("client_userauth.c: make_exc_client_userauth\n");
402

403
  self->super.parent = parent;
404
  self->super.raise = do_exc_client_userauth;
405
  self->super.context = context;
Niels Möller's avatar
Niels Möller committed
406

407
408
409
  self->state = state;

  return &self->super;
Niels Möller's avatar
Niels Möller committed
410
411
}

412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
/* GABA:
   (class
     (name exc_userauth_disconnect)
     (super exception_handler)
     (vars
       (connection object ssh_connection)))
*/

/* Disconnects the connection if user authentication fails. */
static void
do_exc_userauth_disconnect(struct exception_handler *s,
			   const struct exception *e)
{
  CAST(exc_userauth_disconnect, self, s);
  
  if (e->type == EXC_USERAUTH)
    {
      werror("User authentication failed.\n");
      EXCEPTION_RAISE(self->connection->e,
		      make_protocol_exception(SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
					      NULL));
    }

  EXCEPTION_RAISE(s->parent, e);
}

static struct exception_handler *
make_exc_userauth_disconnect(struct ssh_connection *connection,
			     struct exception_handler *parent,
			     const char *context)
{
  NEW(exc_userauth_disconnect, self);

  trace("client_userauth.c: make_exc_userauth_disconnect\n");

  self->super.parent = parent;
  self->super.raise = do_exc_userauth_disconnect;
  self->super.context = context;

  self->connection = connection;
  return &self->super;
}

Niels Möller's avatar
Niels Möller committed
455
456
457
458
static void
do_client_userauth(struct command *s,
		   struct lsh_object *x,
		   struct command_continuation *c,
459
		   struct exception_handler *e)
Niels Möller's avatar
Niels Möller committed
460
{
461
462
463
  CAST(client_userauth, self, s);
  CAST(ssh_connection, connection, x);
  
464
465
466
  struct client_userauth_state *state
    = make_client_userauth_state(self, connection);

467
468
  trace("client_userauth.c: do_client_userauth\n");

Niels Möller's avatar
Niels Möller committed
469
  connection->dispatch[SSH_MSG_USERAUTH_SUCCESS]
470
    = make_success_handler(c);
Niels Möller's avatar
Niels Möller committed
471
  connection->dispatch[SSH_MSG_USERAUTH_FAILURE]
472
    = make_failure_handler(state);
Niels Möller's avatar
Niels Möller committed
473
  connection->dispatch[SSH_MSG_USERAUTH_BANNER]
474
    = &userauth_banner_handler;
Niels Möller's avatar
Niels Möller committed
475

476
477
478
479
  assert(LIST_LENGTH(self->methods));
  {
    CAST_SUBTYPE(client_userauth_method, method,
		 LIST(self->methods)[0]);
480
481
482
483
484
485
486
487
    state->failure
      = CLIENT_USERAUTH_LOGIN(method, self,
			      connection,
			      make_exc_client_userauth
			      (state,
			       make_exc_userauth_disconnect
			       (connection, e, HANDLER_CONTEXT),
			       HANDLER_CONTEXT));
488
  }
Niels Möller's avatar
Niels Möller committed
489
490
}

491

Niels Möller's avatar
Niels Möller committed
492
493
494
495
struct command *
make_client_userauth(struct lsh_string *username,
		     int service_name,
		     struct object_list *methods)
Niels Möller's avatar
Niels Möller committed
496
{
497
  NEW(client_userauth, self);
Niels Möller's avatar
Niels Möller committed
498

499
  self->super.call = do_client_userauth;
500
  self->username = local_to_utf8(username, 1);
501
  self->service_name = service_name;
Balázs Scheidler's avatar
Balázs Scheidler committed
502
  self->methods = methods;
Niels Möller's avatar
Niels Möller committed
503

504
  return &self->super;
Niels Möller's avatar
Niels Möller committed
505
}
506

507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571

/* None authentication */
/* GABA:
   (class
     (name client_none_state)
     (super client_userauth_failure)
     (vars
       (e object exception_handler)))
*/

static void
do_none_failure(struct client_userauth_failure *s, int again)
{
  CAST(client_none_state, self, s);
  
  static const struct exception need_auth =
    STATIC_EXCEPTION(EXC_USERAUTH, "Need real authentication.");

  trace("client_userauth.c: do_none_failure\n");

  if (again)
    werror("Odd. Server says we should try the `none'authentication method again.\n");

  EXCEPTION_RAISE(self->e, &need_auth);
}

static struct client_userauth_failure *
make_client_none_state(struct exception_handler *e)
{
  NEW(client_none_state, self);

  trace("client_userauth.c: make_client_none_state\n");

  self->super.failure = do_none_failure;
  self->e = e;

  return &self->super;
}

static struct client_userauth_failure *
do_none_login(struct client_userauth_method *s UNUSED,
              struct client_userauth *userauth,
              struct ssh_connection *connection,
              struct exception_handler *e)
{
  trace("client_userauth.c: do_none_login\n");

  C_WRITE(connection,
          format_userauth_none(userauth->username,
                               userauth->service_name));
  
  return make_client_none_state(e);
}

static struct client_userauth_method
client_userauth_none =
  { STATIC_HEADER, ATOM_NONE, do_none_login };

struct client_userauth_method *
make_client_none_auth(void)
{
  return &client_userauth_none;
}


572
573
574
575
/* Password authentication */

#define MAX_PASSWD 100

576
577
578
579
580
581
/* GABA:
   (class
     (name client_password_state)
     (super client_userauth_failure)
     (vars
       (userauth object client_userauth)
582
       (tty object interact)
583
584
585
586
587
588
       ; Have we tried the empty password already?
       (tried_empty_passwd . int)
       (connection object ssh_connection)
       (e object exception_handler)))
*/

589
static void
590
send_password(struct client_password_state *state)
591
592
{
  struct lsh_string *passwd
593
594
595
    = INTERACT_READ_PASSWORD(state->tty, MAX_PASSWD,
			     ssh_format("Password for %lS: ",
					state->userauth->username), 1);
596
597

  if (passwd)
598
599
600
601
    {
      /* Password empty? */
      if (!passwd->length)
	{
602
	  /* NOTE: At least on some systems, the getpass function
603
604
605
606
607
	   * sets the tty to raw mode, disabling ^C, ^D and the like.
	   *
	   * To be a little friendlier, we stop asking if the users
	   * gives us the empty password twice.
	   */
608
	  state->tried_empty_passwd++;
609
	}
610
611

      C_WRITE(state->connection,
612
	      format_userauth_password(state->userauth->username,
613
614
615
				       state->userauth->service_name,
				       local_to_utf8(passwd, 1),
				       1));
616
    }
617
618
  else
    {
619
620
      /* If the user aborts the password dialogue (by pressing ^D at
       * the terminal, or by closing a password popup window,
621
       * whatever), read_password should return NULL, and we should
622
       * skip to the next authentication method. */
623
624
625
      static const struct exception no_passwd =
	STATIC_EXCEPTION(EXC_USERAUTH, "No password supplied.");

626
      EXCEPTION_RAISE(state->e, &no_passwd);
627
628
629
630
631
    }
}


static void
632
do_password_failure(struct client_userauth_failure *s, int again)
633
634
635
{
  CAST(client_password_state, self, s);

636
637
  trace("client_userauth.c: do_password_failure\n");

638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
  if (again)
    {
      if (self->tried_empty_passwd >= 2)
	{
	  static const struct exception no_passwd =
	    STATIC_EXCEPTION(EXC_USERAUTH, "No password supplied.");
	  
	  EXCEPTION_RAISE(self->e, &no_passwd);
	}
      else
	send_password(self);
    }
  else
    {
      static const struct exception passwd_not_useful =
	STATIC_EXCEPTION(EXC_USERAUTH, "password authentication not useful.");

      EXCEPTION_RAISE(self->e, &passwd_not_useful);
    }
657
658
}

659
static struct client_password_state *
660
make_client_password_state(struct client_userauth *userauth,
661
			   struct interact *tty,
662
663
664
665
			   struct ssh_connection *connection,
			   struct exception_handler *e)
{
  NEW(client_password_state, self);
666
667
668

  trace("client_userauth.c: make_client_password_state\n");

669
670
  self->super.failure = do_password_failure;
  self->userauth = userauth;
671
  self->tty = tty;
672
  self->tried_empty_passwd = 0;
673
674
675
  self->connection = connection;
  self->e = e;

676
  return self;
677
678
}

679
680
681
682
683
684
685
686
/* GABA:
   (class
     (name client_password_method)
     (super client_userauth_method)
     (vars
       (tty object interact)))
*/

687

688
static struct client_userauth_failure *
689
do_password_login(struct client_userauth_method *s,
690
691
692
693
		  struct client_userauth *userauth,
		  struct ssh_connection *connection,
		  struct exception_handler *e)
{
694
695
  CAST(client_password_method, self, s);
  
696
  struct client_password_state *state
697
698
    = make_client_password_state(userauth, self->tty,
				 connection, e);
699
700
701

  trace("client_userauth.c: do_password_login\n");

702
703
704
  send_password(state);
  
  return &state->super;
705
706
707
}

struct client_userauth_method *
708
make_client_password_auth(struct interact *tty)
709
{
710
711
712
713
  NEW(client_password_method, self);
  self->super.type = ATOM_PASSWORD;
  self->super.login = do_password_login;
  self->tty = tty;
714
  
715
  return &self->super;
716
717
718
719
720
}


/* Publickey authentication. */

721
722
723
724
725
726
727
728
729
730
731
732
/* We first send a set of publickey authentication requests for the
 * available keys. This is analogous to unlocking a door by first
 * examining the keys on one's keyring to see if any of them can be
 * inserted into the lock. '
 *
 * FIXME: At this stage it is preferable to use spki hashed public
 * keys rather than the public keys themselves.
 *
 * Next we wait for SSH_MSH_USERAUTH_FAILURE or SSH_MSG_USERAUTH_PK_OK
 * messages. If any of the keys is recognized, we compute a signature
 * and send it to the server (analogous to inserting the key into the
 * lock and trying to turn it around). */
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752

/* GABA:
   (class
     (name client_publickey_method)
     (super client_userauth_method)
     (vars
       (keys object object_list)))
*/

/* GABA:
   (class
     (name client_publickey_state)
     (super client_userauth_failure)
     (vars
       (userauth object client_userauth)
       (connection object ssh_connection)
       (keys object object_list)
       ; Number of keys for which we have received either
       ; a USERAUTH_FAILURE or USERAUTH_PK_OK message.
       (done . UINT32)
753
754
755
       ; Number of keys for which we have computed and sent a signature,
       ; and not yet received any failure.
       (pending . UINT32)
756
757
758
759
760
761
       (e object exception_handler)))
*/

static void
client_publickey_next(struct client_publickey_state *state)
{
762
763
764
765
  static const struct exception publickey_auth_failed =
    STATIC_EXCEPTION(EXC_USERAUTH, "Public key userauth failed.");

  if (state->done < LIST_LENGTH(state->keys))
766
    {
767
768
769
770
771
772
773
774
775
      /* We received a response on a query request. */
      
      state->done++;
      if (state->done < LIST_LENGTH(state->keys))
	/* We are still waiting for SSH_MSG_USERAUTH_FAILURE or
	 * SSH_MSG_USERAUTH_PK_OK */
	return;

      /* We have got a response on the final query request. */
776
      state->connection->dispatch[SSH_MSG_USERAUTH_PK_OK]
777
	= &connection_fail_handler;
778
    }
779
780
781
782
783
784
785
786
787
788
789
  else
    {
      /* We received a response (actually, a failure) on a request
       * that included a signature. */
      assert(state->pending);
      state->pending--;
    }

  if (!state->pending)
    /* All failed. */
    EXCEPTION_RAISE(state->e, &publickey_auth_failed);
790
791
792
}

static void
793
do_publickey_failure(struct client_userauth_failure *s, int again UNUSED)
794
795
796
{
  CAST(client_publickey_state, self, s);

797
798
799
  /* NOTE: We have several pending requests, so we have to get all the
   * replies before skipping to the next method. That's why the again
   * argument must be ignored. */
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
  client_publickey_next(self);
}
  
static struct client_publickey_state *
make_client_publickey_state(struct client_userauth *userauth,
			    struct ssh_connection *connection,
			    struct object_list *keys,
			    struct exception_handler *e)
{
  NEW(client_publickey_state, self);
  self->super.failure = do_publickey_failure;
  self->userauth = userauth;
  self->connection = connection;
  self->keys = keys;
  self->done = 0;
  self->pending = 0;
  self->e = e;

  return self;
}

/* GABA:
   (class
     (name userauth_pk_ok_handler)
     (super packet_handler)
     (vars
826
       (state object client_publickey_state))) */
827
828
829
830
831
832
833
834
835
836
837
838
839
  
static void 
do_userauth_pk_ok(struct packet_handler *s,
		  struct ssh_connection *connection,
		  struct lsh_string *packet UNUSED)
{
  CAST(userauth_pk_ok_handler, self, s);

  struct simple_buffer buffer;

  unsigned msg_number;
  int algorithm;
  UINT32 keyblob_length;
Niels Möller's avatar
Niels Möller committed
840
  const UINT8 *keyblob;
841
842
843
844
845
846
847
848
849
850
851
852
853

  simple_buffer_init(&buffer, packet->length, packet->data);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_USERAUTH_PK_OK)
      && parse_atom(&buffer, &algorithm)
      && parse_string(&buffer, &keyblob_length, &keyblob)
      && parse_eod(&buffer))
    {
      CAST(keypair, key, LIST(self->state->keys)[self->state->done]);
      verbose("SSH_MSG_USERAUTH_PK_OK received\n");
	  
      if ( (key->type == algorithm)
854
	   && lsh_string_eq_l(key->public, keyblob_length, keyblob) )
855
856
857
	{
	  struct lsh_string *request;
	  struct lsh_string *signed_data;
858

859
860
#if DATAFELLOWS_WORKAROUNDS
	  if (connection->peer_flags & PEER_USERAUTH_REQUEST_KLUDGE)
861
	    request = format_userauth_publickey(self->state->userauth->username,
862
863
864
865
						ATOM_SSH_USERAUTH,
						key->type,
						key->public);
	  else
866
#endif /* DATAFELLOWS_WORKAROUNDS */ 
867
	    request = format_userauth_publickey(self->state->userauth->username,
868
869
870
						self->state->userauth->service_name,
						key->type,
						key->public);
871

872
873
874
	  signed_data = ssh_format("%S%lS", connection->session_id, request);
	  request = ssh_format("%flS%fS", 
			       request, 
875
			       SIGN(key->private, key->type, signed_data->length, signed_data->data));
876
877
	  lsh_string_free(signed_data);
	  C_WRITE(connection, request);
878
	  self->state->pending++;
879
880
881
882
883
884
885
	}
      else
	werror("client_userauth.c: Unexpected key in USERAUTH_PK_OK message.\n");

      client_publickey_next(self->state);
    }
  else
886
    PROTOCOL_ERROR(self->state->e, "Invalid USERAUTH_PK_OK message");
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
}

static struct packet_handler *
make_pk_ok_handler(struct client_publickey_state *state)
{
  NEW(userauth_pk_ok_handler, self);

  self->super.handler = do_userauth_pk_ok;
  self->state = state;

  return &self->super;
}

static struct client_userauth_failure *
do_publickey_login(struct client_userauth_method *s,
		   struct client_userauth *userauth,
		   struct ssh_connection *connection,
		   struct exception_handler *e)
{
  CAST(client_publickey_method, self, s);

  assert(LIST_LENGTH(self->keys));
  
    
911
912
913
914
915
916
917
  {
    struct client_publickey_state *state =
      make_client_publickey_state(userauth,
				  connection,
				  self->keys,
				  e);
    unsigned i;
918
      
919
920
921
922
    for (i = 0; i < LIST_LENGTH(self->keys); i++)
      {
	CAST(keypair, key, LIST(self->keys)[i]);

Niels Möller's avatar
Niels Möller committed
923
924
	/* NOTE: The PEER_USERAUTH_REQUEST_KLUDGE only applies to the
	 * signed data. */
925
	C_WRITE(connection, 
926
		format_userauth_publickey_query(userauth->username,
927
928
929
930
931
932
						userauth->service_name,
						key->type, key->public));
      }
    connection->dispatch[SSH_MSG_USERAUTH_PK_OK] = make_pk_ok_handler(state);
    return &state->super;
  }
933
934
935
936
937
938
939
940
941
942
943
944
}

struct client_userauth_method *
make_client_publickey_auth(struct object_list *keys)
{
  NEW(client_publickey_method, self);

  self->super.login = do_publickey_login;
  self->keys = keys;
  
  return &self->super;
}