fixed crash when bogus payload (auth_data == NULL) was received.
[silc.git] / lib / silccore / silcauth.c
1 /*
2
3   silcauth.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2002 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "silcincludes.h"
23 #include "silcauth.h"
24
25 /******************************************************************************
26
27                            Authentication Payload
28
29 ******************************************************************************/
30
31 /* Authentication Payload structure */
32 struct SilcAuthPayloadStruct {
33   SilcUInt16 len;
34   SilcUInt16 auth_method;
35   SilcUInt16 random_len;
36   unsigned char *random_data;
37   SilcUInt16 auth_len;
38   unsigned char *auth_data;
39 };
40
41 /* Parses and returns Authentication Payload */
42
43 SilcAuthPayload silc_auth_payload_parse(const unsigned char *data,
44                                         SilcUInt32 data_len)
45 {
46   SilcBufferStruct buffer;
47   SilcAuthPayload newp;
48   int ret;
49
50   SILC_LOG_DEBUG(("Parsing Authentication Payload"));
51
52   silc_buffer_set(&buffer, (unsigned char *)data, data_len);
53   newp = silc_calloc(1, sizeof(*newp));
54   if (!newp)
55     return NULL;
56
57   /* Parse the payload */
58   ret = silc_buffer_unformat(&buffer,
59                              SILC_STR_UI_SHORT(&newp->len),
60                              SILC_STR_UI_SHORT(&newp->auth_method),
61                              SILC_STR_UI16_NSTRING_ALLOC(&newp->random_data,
62                                                          &newp->random_len),
63                              SILC_STR_UI16_NSTRING_ALLOC(&newp->auth_data,
64                                                          &newp->auth_len),
65                              SILC_STR_END);
66   if (ret == -1) {
67     silc_free(newp);
68     return NULL;
69   }
70
71   if (newp->len != buffer.len) {
72     silc_auth_payload_free(newp);
73     return NULL;
74   }
75
76   /* If password authentication, random data must not be set */
77   if (newp->auth_method == SILC_AUTH_PASSWORD && newp->random_len) {
78     silc_auth_payload_free(newp);
79     return NULL;
80   }
81
82   /* If public key authentication, random data must be at least 128 bytes */
83   if (newp->auth_method == SILC_AUTH_PUBLIC_KEY && newp->random_len < 128) {
84     silc_auth_payload_free(newp);
85     return NULL;
86   }
87
88   return newp;
89 }
90
91 /* Encodes authentication payload into buffer and returns it */
92
93 SilcBuffer silc_auth_payload_encode(SilcAuthMethod method,
94                                     const unsigned char *random_data,
95                                     SilcUInt16 random_len,
96                                     const unsigned char *auth_data,
97                                     SilcUInt16 auth_len)
98 {
99   SilcBuffer buffer;
100   SilcUInt32 len;
101   unsigned char *autf8 = NULL;
102   SilcUInt32 autf8_len;
103
104   SILC_LOG_DEBUG(("Encoding Authentication Payload"));
105
106   /* Passphrase MUST be UTF-8 encoded, encode if it is not */
107   if (method == SILC_AUTH_PASSWORD && !silc_utf8_valid(auth_data, auth_len)) {
108     autf8_len = silc_utf8_encoded_len(auth_data, auth_len, 0);
109     if (!autf8_len)
110       return NULL;
111     autf8 = silc_calloc(autf8_len, sizeof(*autf8));
112     auth_len = silc_utf8_encode(auth_data, auth_len, 0, autf8, autf8_len);
113     auth_data = (const unsigned char *)autf8;
114   }
115
116   len = 2 + 2 + 2 + random_len + 2 + auth_len;
117   buffer = silc_buffer_alloc_size(len);
118   if (!buffer) {
119     silc_free(autf8);
120     return NULL;
121   }
122
123   silc_buffer_format(buffer,
124                      SILC_STR_UI_SHORT(len),
125                      SILC_STR_UI_SHORT(method),
126                      SILC_STR_UI_SHORT(random_len),
127                      SILC_STR_UI_XNSTRING(random_data, random_len),
128                      SILC_STR_UI_SHORT(auth_len),
129                      SILC_STR_UI_XNSTRING(auth_data, auth_len),
130                      SILC_STR_END);
131
132   silc_free(autf8);
133   return buffer;
134 }
135
136 /* Frees authentication payload. */
137
138 void silc_auth_payload_free(SilcAuthPayload payload)
139 {
140   if (payload) {
141     if (payload->random_data) {
142       memset(payload->random_data, 0, payload->random_len);
143       silc_free(payload->random_data);
144     }
145     if (payload->auth_data) {
146       memset(payload->auth_data, 0, payload->auth_len);
147       silc_free(payload->auth_data);
148     }
149     silc_free(payload);
150   }
151 }
152
153 /* Get authentication method */
154
155 SilcAuthMethod silc_auth_get_method(SilcAuthPayload payload)
156 {
157   return payload->auth_method;
158 }
159
160 /* Get the authentication data. If this is passphrase it is UTF-8 encoded. */
161
162 unsigned char *silc_auth_get_data(SilcAuthPayload payload,
163                                   SilcUInt32 *auth_len)
164 {
165   if (auth_len)
166     *auth_len = payload->auth_len;
167
168   return payload->auth_data;
169 }
170
171 /******************************************************************************
172
173                            Authentication Routines
174
175 ******************************************************************************/
176
177 /* Encodes the authentication data for hashing and signing as the protocol
178    dictates. */
179
180 static unsigned char *
181 silc_auth_public_key_encode_data(SilcPublicKey public_key,
182                                  const unsigned char *random,
183                                  SilcUInt32 random_len, const void *id,
184                                  SilcIdType type, SilcUInt32 *ret_len)
185 {
186   SilcBuffer buf;
187   unsigned char *pk, *id_data, *ret;
188   SilcUInt32 pk_len, id_len;
189
190   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
191   if (!pk)
192     return NULL;
193
194   id_data = silc_id_id2str(id, type);
195   if (!id_data) {
196     silc_free(pk);
197     return NULL;
198   }
199   id_len = silc_id_get_len(id, type);
200
201   buf = silc_buffer_alloc_size(random_len + id_len + pk_len);
202   if (!buf) {
203     silc_free(pk);
204     silc_free(id_data);
205     return NULL;
206   }
207   silc_buffer_format(buf,
208                      SILC_STR_UI_XNSTRING(random, random_len),
209                      SILC_STR_UI_XNSTRING(id_data, id_len),
210                      SILC_STR_UI_XNSTRING(pk, pk_len),
211                      SILC_STR_END);
212
213   ret = silc_memdup(buf->data, buf->len);
214   if (!ret)
215     return NULL;
216
217   if (ret_len)
218     *ret_len = buf->len;
219
220   silc_buffer_free(buf);
221   silc_free(id_data);
222   silc_free(pk);
223
224   return ret;
225 }
226
227 /* Generates Authentication Payload with authentication data. This is used
228    to do public key based authentication. This generates the random data
229    and the actual authentication data. Returns NULL on error. */
230
231 SilcBuffer silc_auth_public_key_auth_generate(SilcPublicKey public_key,
232                                               SilcPrivateKey private_key,
233                                               SilcRng rng, SilcHash hash,
234                                               const void *id, SilcIdType type)
235 {
236   unsigned char *random;
237   unsigned char auth_data[1024];
238   SilcUInt32 auth_len;
239   unsigned char *tmp;
240   SilcUInt32 tmp_len;
241   SilcBuffer buf;
242   SilcPKCS pkcs;
243
244   SILC_LOG_DEBUG(("Generating Authentication Payload with data"));
245
246   /* Get 256 bytes of random data */
247   if (rng)
248     random = silc_rng_get_rn_data(rng, 256);
249   else
250     random = silc_rng_global_get_rn_data(256);
251   if (!random)
252     return NULL;
253
254   /* Encode the auth data */
255   tmp = silc_auth_public_key_encode_data(public_key, random, 256, id, type,
256                                          &tmp_len);
257   if (!tmp)
258     return NULL;
259
260   /* Allocate PKCS object */
261   if (!silc_pkcs_alloc(public_key->name, &pkcs)) {
262     memset(tmp, 0, tmp_len);
263     silc_free(tmp);
264     return NULL;
265   }
266   silc_pkcs_public_key_set(pkcs, public_key);
267   silc_pkcs_private_key_set(pkcs, private_key);
268
269   /* Compute the hash and the signature. */
270   if (!silc_pkcs_sign_with_hash(pkcs, hash, tmp, tmp_len, auth_data,
271                                 &auth_len)) {
272     memset(random, 0, 256);
273     memset(tmp, 0, tmp_len);
274     silc_free(tmp);
275     silc_free(random);
276     silc_pkcs_free(pkcs);
277     return NULL;
278   }
279
280   /* Encode Authentication Payload */
281   buf = silc_auth_payload_encode(SILC_AUTH_PUBLIC_KEY, random, 256,
282                                  auth_data, auth_len);
283
284   memset(tmp, 0, tmp_len);
285   memset(auth_data, 0, sizeof(auth_data));
286   memset(random, 0, 256);
287   silc_free(tmp);
288   silc_free(random);
289   silc_pkcs_free(pkcs);
290
291   return buf;
292 }
293
294 /* Verifies the authentication data. Returns TRUE if authentication was
295    successful. */
296
297 bool silc_auth_public_key_auth_verify(SilcAuthPayload payload,
298                                       SilcPublicKey public_key, SilcHash hash,
299                                       const void *id, SilcIdType type)
300 {
301   unsigned char *tmp;
302   SilcUInt32 tmp_len;
303   SilcPKCS pkcs;
304
305   SILC_LOG_DEBUG(("Verifying authentication data"));
306
307   /* Encode auth data */
308   tmp = silc_auth_public_key_encode_data(public_key, payload->random_data,
309                                          payload->random_len,
310                                          id, type, &tmp_len);
311   if (!tmp) {
312     SILC_LOG_DEBUG(("Authentication failed"));
313     return FALSE;
314   }
315
316   /* Allocate PKCS object */
317   if (!silc_pkcs_alloc(public_key->name, &pkcs)) {
318     memset(tmp, 0, tmp_len);
319     silc_free(tmp);
320     return FALSE;
321   }
322   silc_pkcs_public_key_set(pkcs, public_key);
323
324   /* Verify the authentication data */
325   if (!silc_pkcs_verify_with_hash(pkcs, hash, payload->auth_data,
326                                   payload->auth_len, tmp, tmp_len)) {
327
328     memset(tmp, 0, tmp_len);
329     silc_free(tmp);
330     silc_pkcs_free(pkcs);
331     SILC_LOG_DEBUG(("Authentication failed"));
332     return FALSE;
333   }
334
335   memset(tmp, 0, tmp_len);
336   silc_free(tmp);
337   silc_pkcs_free(pkcs);
338
339   SILC_LOG_DEBUG(("Authentication successful"));
340
341   return TRUE;
342 }
343
344 /* Same as above but the payload is not parsed yet. This will parse it. */
345
346 bool silc_auth_public_key_auth_verify_data(const unsigned char *payload,
347                                            SilcUInt32 payload_len,
348                                            SilcPublicKey public_key,
349                                            SilcHash hash,
350                                            const void *id, SilcIdType type)
351 {
352   SilcAuthPayload auth_payload;
353   int ret;
354
355   auth_payload = silc_auth_payload_parse(payload, payload_len);
356   if (!auth_payload) {
357     SILC_LOG_DEBUG(("Authentication failed"));
358     return FALSE;
359   }
360
361   ret = silc_auth_public_key_auth_verify(auth_payload, public_key, hash,
362                                          id, type);
363
364   silc_auth_payload_free(auth_payload);
365
366   return ret;
367 }
368
369 /* Verifies the authentication data directly from the Authentication
370    Payload. Supports all authentication methods. If the authentication
371    method is passphrase based then the `auth_data' and `auth_data_len'
372    are the passphrase and its length. If the method is public key
373    authentication then the `auth_data' is the SilcPublicKey and the
374    `auth_data_len' is ignored. */
375
376 bool silc_auth_verify(SilcAuthPayload payload, SilcAuthMethod auth_method,
377                       const void *auth_data, SilcUInt32 auth_data_len,
378                       SilcHash hash, const void *id, SilcIdType type)
379 {
380   SILC_LOG_DEBUG(("Verifying authentication"));
381
382   if (auth_method != payload->auth_method)
383     return FALSE;
384
385   switch (payload->auth_method) {
386   case SILC_AUTH_NONE:
387     /* No authentication */
388     SILC_LOG_DEBUG(("No authentication required"));
389     return TRUE;
390
391   case SILC_AUTH_PASSWORD:
392     /* Passphrase based authentication. The `pkcs', `hash', `id' and `type'
393        arguments are not needed. */
394     /* Carefully check that the auth_data field of the payload is not empty
395        (len=0), which seems to be a legal packet but would crash the
396        application. Maybe such packet should be dropped. -Johnny 2002/14/4 */
397     if ((payload->auth_len == 0) || !auth_data)
398       break;
399
400     /* if lengths mismatch, avoid comparing unallocated memory locations */
401     if (payload->auth_len != auth_data_len)
402       break;
403     if (!memcmp(payload->auth_data, auth_data, auth_data_len)) {
404       SILC_LOG_DEBUG(("Passphrase Authentication successful"));
405       return TRUE;
406     }
407     break;
408
409   case SILC_AUTH_PUBLIC_KEY:
410     /* Public key based authentication */
411     return silc_auth_public_key_auth_verify(payload, (SilcPublicKey)auth_data,
412                                             hash, id, type);
413     break;
414
415   default:
416     break;
417   }
418
419   SILC_LOG_DEBUG(("Authentication failed"));
420
421   return FALSE;
422 }
423
424 /* Same as above but parses the authentication payload before verify. */
425
426 bool silc_auth_verify_data(const unsigned char *payload,
427                            SilcUInt32 payload_len,
428                            SilcAuthMethod auth_method, const void *auth_data,
429                            SilcUInt32 auth_data_len, SilcHash hash,
430                            const void *id, SilcIdType type)
431 {
432   SilcAuthPayload auth_payload;
433   bool ret;
434
435   auth_payload = silc_auth_payload_parse(payload, payload_len);
436   if (!auth_payload || (auth_payload->auth_len == 0))
437     return FALSE;
438
439   ret = silc_auth_verify(auth_payload, auth_method, auth_data, auth_data_len,
440                          hash, id, type);
441
442   silc_auth_payload_free(auth_payload);
443
444   return ret;
445 }
446
447 /******************************************************************************
448
449                             Key Agreement Payload
450
451 ******************************************************************************/
452
453 /* The Key Agreement protocol structure */
454 struct SilcKeyAgreementPayloadStruct {
455   SilcUInt16 hostname_len;
456   unsigned char *hostname;
457   SilcUInt32 port;
458 };
459
460 /* Parses and returns an allocated Key Agreement payload. */
461
462 SilcKeyAgreementPayload
463 silc_key_agreement_payload_parse(const unsigned char *payload,
464                                  SilcUInt32 payload_len)
465 {
466   SilcBufferStruct buffer;
467   SilcKeyAgreementPayload newp;
468   int ret;
469
470   SILC_LOG_DEBUG(("Parsing Key Agreement Payload"));
471
472   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
473   newp = silc_calloc(1, sizeof(*newp));
474   if (!newp)
475     return NULL;
476
477   /* Parse the payload */
478   ret = silc_buffer_unformat(&buffer,
479                              SILC_STR_UI16_NSTRING_ALLOC(&newp->hostname,
480                                                          &newp->hostname_len),
481                              SILC_STR_UI_INT(&newp->port),
482                              SILC_STR_END);
483   if (ret == -1) {
484     silc_free(newp);
485     return NULL;
486   }
487
488   return newp;
489 }
490
491 /* Encodes the Key Agreement protocol and returns the encoded buffer */
492
493 SilcBuffer silc_key_agreement_payload_encode(const char *hostname,
494                                              SilcUInt32 port)
495 {
496   SilcBuffer buffer;
497   SilcUInt32 len = hostname ? strlen(hostname) : 0;
498
499   SILC_LOG_DEBUG(("Encoding Key Agreement Payload"));
500
501   buffer = silc_buffer_alloc_size(2 + len + 4);
502   if (!buffer)
503     return NULL;
504   silc_buffer_format(buffer,
505                      SILC_STR_UI_SHORT(len),
506                      SILC_STR_UI_XNSTRING(hostname, len),
507                      SILC_STR_UI_INT(port),
508                      SILC_STR_END);
509
510   return buffer;
511 }
512
513 /* Frees the Key Agreement protocol */
514
515 void silc_key_agreement_payload_free(SilcKeyAgreementPayload payload)
516 {
517   if (payload) {
518     silc_free(payload->hostname);
519     silc_free(payload);
520   }
521 }
522
523 /* Returns the hostname in the payload */
524
525 char *silc_key_agreement_get_hostname(SilcKeyAgreementPayload payload)
526 {
527   return payload->hostname;
528 }
529
530 /* Returns the port in the payload */
531
532 SilcUInt32 silc_key_agreement_get_port(SilcKeyAgreementPayload payload)
533 {
534   return payload->port;
535 }