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@poseidon.pspt.fi>
6
7   Copyright (C) 2001 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   return newp;
83 }
84
85 /* Encodes authentication payload into buffer and returns it */
86
87 SilcBuffer silc_auth_payload_encode(SilcAuthMethod method,
88                                     const unsigned char *random_data,
89                                     SilcUInt16 random_len,
90                                     const unsigned char *auth_data,
91                                     SilcUInt16 auth_len)
92 {
93   SilcBuffer buffer;
94   SilcUInt32 len;
95
96   SILC_LOG_DEBUG(("Encoding Authentication Payload"));
97
98   len = 2 + 2 + 2 + random_len + 2 + auth_len;
99   buffer = silc_buffer_alloc_size(len);
100   if (!buffer)
101     return NULL;
102   silc_buffer_format(buffer,
103                      SILC_STR_UI_SHORT(len),
104                      SILC_STR_UI_SHORT(method),
105                      SILC_STR_UI_SHORT(random_len),
106                      SILC_STR_UI_XNSTRING(random_data, random_len),
107                      SILC_STR_UI_SHORT(auth_len),
108                      SILC_STR_UI_XNSTRING(auth_data, auth_len),
109                      SILC_STR_END);
110
111   return buffer;
112 }
113
114 /* Frees authentication payload. */
115
116 void silc_auth_payload_free(SilcAuthPayload payload)
117 {
118   if (payload) {
119     if (payload->random_data) {
120       memset(payload->random_data, 0, payload->random_len);
121       silc_free(payload->random_data);
122     }
123     if (payload->auth_data) {
124       memset(payload->auth_data, 0, payload->auth_len);
125       silc_free(payload->auth_data);
126     }
127     silc_free(payload);
128   }
129 }
130
131 /* Get authentication method */
132
133 SilcAuthMethod silc_auth_get_method(SilcAuthPayload payload)
134 {
135   return payload->auth_method;
136 }
137
138 /* Get the authentication data */
139
140 unsigned char *silc_auth_get_data(SilcAuthPayload payload,
141                                   SilcUInt32 *auth_len)
142 {
143   if (auth_len)
144     *auth_len = payload->auth_len;
145
146   return payload->auth_data;
147 }
148
149 /******************************************************************************
150
151                            Authentication Routines
152
153 ******************************************************************************/
154
155 /* Encodes the authentication data for hashing and signing as the protocol
156    dictates. */
157
158 static unsigned char *
159 silc_auth_public_key_encode_data(SilcPublicKey public_key, 
160                                  const unsigned char *random,
161                                  SilcUInt32 random_len, const void *id,
162                                  SilcIdType type, SilcUInt32 *ret_len)
163 {
164   SilcBuffer buf;
165   unsigned char *pk, *id_data, *ret;
166   SilcUInt32 pk_len, id_len;
167
168   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
169   if (!pk)
170     return NULL;
171
172   id_data = silc_id_id2str(id, type);
173   if (!id_data) {
174     silc_free(pk);
175     return NULL;
176   }
177   id_len = silc_id_get_len(id, type);
178
179   buf = silc_buffer_alloc_size(random_len + id_len + pk_len);
180   if (!buf) {
181     silc_free(pk);
182     silc_free(id_data);
183     return NULL;
184   }
185   silc_buffer_format(buf,
186                      SILC_STR_UI_XNSTRING(random, random_len),
187                      SILC_STR_UI_XNSTRING(id_data, id_len),
188                      SILC_STR_UI_XNSTRING(pk, pk_len),
189                      SILC_STR_END);
190   
191   ret = silc_memdup(buf->data, buf->len);
192   if (!ret)
193     return NULL;
194
195   if (ret_len)
196     *ret_len = buf->len;
197
198   silc_buffer_free(buf);
199   silc_free(id_data);
200   silc_free(pk);
201
202   return ret;
203 }
204
205 /* Generates Authentication Payload with authentication data. This is used
206    to do public key based authentication. This generates the random data
207    and the actual authentication data. Returns NULL on error. */
208
209 SilcBuffer silc_auth_public_key_auth_generate(SilcPublicKey public_key,
210                                               SilcPrivateKey private_key,
211                                               SilcRng rng, SilcHash hash,
212                                               const void *id, SilcIdType type)
213 {
214   unsigned char *random;
215   unsigned char auth_data[1024];
216   SilcUInt32 auth_len;
217   unsigned char *tmp;
218   SilcUInt32 tmp_len;
219   SilcBuffer buf;
220   SilcPKCS pkcs;
221
222   SILC_LOG_DEBUG(("Generating Authentication Payload with data"));
223
224   /* Get 256 bytes of random data */
225   if (rng)
226     random = silc_rng_get_rn_data(rng, 256);
227   else
228     random = silc_rng_global_get_rn_data(256);
229   if (!random)
230     return NULL;
231   
232   /* Encode the auth data */
233   tmp = silc_auth_public_key_encode_data(public_key, random, 256, id, type, 
234                                          &tmp_len);
235   if (!tmp)
236     return NULL;
237
238   /* Allocate PKCS object */
239   if (!silc_pkcs_alloc(public_key->name, &pkcs)) {
240     memset(tmp, 0, tmp_len);
241     silc_free(tmp);
242     return NULL;
243   }
244   silc_pkcs_public_key_set(pkcs, public_key);
245   silc_pkcs_private_key_set(pkcs, private_key);
246
247   /* Compute the hash and the signature. */
248   if (!silc_pkcs_sign_with_hash(pkcs, hash, tmp, tmp_len, auth_data,
249                                 &auth_len)) {
250     memset(random, 0, 256);
251     memset(tmp, 0, tmp_len);
252     silc_free(tmp);
253     silc_free(random);
254     silc_pkcs_free(pkcs);
255     return NULL;
256   }
257
258   /* Encode Authentication Payload */
259   buf = silc_auth_payload_encode(SILC_AUTH_PUBLIC_KEY, random, 256,
260                                  auth_data, auth_len);
261
262   memset(tmp, 0, tmp_len);
263   memset(auth_data, 0, sizeof(auth_data));
264   memset(random, 0, 256);
265   silc_free(tmp);
266   silc_free(random);
267   silc_pkcs_free(pkcs);
268
269   return buf;
270 }
271
272 /* Verifies the authentication data. Returns TRUE if authentication was
273    successful. */
274
275 bool silc_auth_public_key_auth_verify(SilcAuthPayload payload,
276                                       SilcPublicKey public_key, SilcHash hash,
277                                       const void *id, SilcIdType type)
278 {
279   unsigned char *tmp;
280   SilcUInt32 tmp_len;
281   SilcPKCS pkcs;
282
283   SILC_LOG_DEBUG(("Verifying authentication data"));
284
285   /* Encode auth data */
286   tmp = silc_auth_public_key_encode_data(public_key, payload->random_data, 
287                                          payload->random_len, 
288                                          id, type, &tmp_len);
289   if (!tmp) {
290     SILC_LOG_DEBUG(("Authentication failed"));
291     return FALSE;
292   }
293
294   /* Allocate PKCS object */
295   if (!silc_pkcs_alloc(public_key->name, &pkcs)) {
296     memset(tmp, 0, tmp_len);
297     silc_free(tmp);
298     return FALSE;
299   }
300   silc_pkcs_public_key_set(pkcs, public_key);
301
302   /* Verify the authentication data */
303   if (!silc_pkcs_verify_with_hash(pkcs, hash, payload->auth_data,
304                                   payload->auth_len, tmp, tmp_len)) {
305
306     memset(tmp, 0, tmp_len);
307     silc_free(tmp);
308     silc_pkcs_free(pkcs);
309     SILC_LOG_DEBUG(("Authentication failed"));
310     return FALSE;
311   }
312
313   memset(tmp, 0, tmp_len);
314   silc_free(tmp);
315   silc_pkcs_free(pkcs);
316
317   SILC_LOG_DEBUG(("Authentication successful"));
318
319   return TRUE;
320 }
321
322 /* Same as above but the payload is not parsed yet. This will parse it. */
323
324 bool silc_auth_public_key_auth_verify_data(const unsigned char *payload,
325                                            SilcUInt32 payload_len,
326                                            SilcPublicKey public_key, 
327                                            SilcHash hash,
328                                            const void *id, SilcIdType type)
329 {
330   SilcAuthPayload auth_payload;
331   int ret;
332
333   auth_payload = silc_auth_payload_parse(payload, payload_len);
334   if (!auth_payload) {
335     SILC_LOG_DEBUG(("Authentication failed"));
336     return FALSE;
337   }
338
339   ret = silc_auth_public_key_auth_verify(auth_payload, public_key, hash, 
340                                          id, type);
341
342   silc_auth_payload_free(auth_payload);
343
344   return ret;
345 }
346
347 /* Verifies the authentication data directly from the Authentication 
348    Payload. Supports all authentication methods. If the authentication
349    method is passphrase based then the `auth_data' and `auth_data_len'
350    are the passphrase and its length. If the method is public key
351    authentication then the `auth_data' is the SilcPublicKey and the
352    `auth_data_len' is ignored. */
353
354 bool silc_auth_verify(SilcAuthPayload payload, SilcAuthMethod auth_method,
355                       const void *auth_data, SilcUInt32 auth_data_len, 
356                       SilcHash hash, const void *id, SilcIdType type)
357 {
358   SILC_LOG_DEBUG(("Verifying authentication"));
359
360   if (auth_method != payload->auth_method)
361     return FALSE;
362
363   switch (payload->auth_method) {
364   case SILC_AUTH_NONE:
365     /* No authentication */
366     SILC_LOG_DEBUG(("No authentication required"));
367     return TRUE;
368
369   case SILC_AUTH_PASSWORD:
370     /* Passphrase based authentication. The `pkcs', `hash', `id' and `type'
371        arguments are not needed. */
372     /* Carefully check that the auth_data field of the payload is not empty
373        (len=0), which seems to be a legal packet but would crash the
374        application. Maybe such packet should be dropped. -Johnny 2002/14/4 */
375     if ((payload->auth_len == 0) || !auth_data)
376       break;
377
378     /* if lengths mismatch, avoid comparing unallocated memory locations */
379     if (payload->auth_len != auth_data_len)
380       break;
381     if (!memcmp(payload->auth_data, auth_data, auth_data_len)) {
382       SILC_LOG_DEBUG(("Passphrase Authentication successful"));
383       return TRUE;
384     }
385     break;
386
387   case SILC_AUTH_PUBLIC_KEY:
388     /* Public key based authentication */
389     return silc_auth_public_key_auth_verify(payload, (SilcPublicKey)auth_data,
390                                             hash, id, type);
391     break;
392
393   default:
394     break;
395   }
396
397   SILC_LOG_DEBUG(("Authentication failed"));
398
399   return FALSE;
400 }
401
402 /* Same as above but parses the authentication payload before verify. */
403
404 bool silc_auth_verify_data(const unsigned char *payload, SilcUInt32 payload_len,
405                            SilcAuthMethod auth_method, const void *auth_data,
406                            SilcUInt32 auth_data_len, SilcHash hash, 
407                            const void *id, SilcIdType type)
408 {
409   SilcAuthPayload auth_payload;
410   int ret;
411
412   auth_payload = silc_auth_payload_parse(payload, payload_len);
413   if (!auth_payload)
414     return FALSE;
415
416   ret = silc_auth_verify(auth_payload, auth_method, auth_data, auth_data_len, 
417                          hash, id, type);
418
419   silc_auth_payload_free(auth_payload);
420
421   return ret;
422 }
423
424 /******************************************************************************
425
426                             Key Agreement Payload
427
428 ******************************************************************************/
429
430 /* The Key Agreement protocol structure */
431 struct SilcKeyAgreementPayloadStruct {
432   SilcUInt16 hostname_len;
433   unsigned char *hostname;
434   SilcUInt32 port;
435 };
436
437 /* Parses and returns an allocated Key Agreement payload. */
438
439 SilcKeyAgreementPayload 
440 silc_key_agreement_payload_parse(const unsigned char *payload,
441                                  SilcUInt32 payload_len)
442 {
443   SilcBufferStruct buffer;
444   SilcKeyAgreementPayload newp;
445   int ret;
446
447   SILC_LOG_DEBUG(("Parsing Key Agreement Payload"));
448
449   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
450   newp = silc_calloc(1, sizeof(*newp));
451   if (!newp)
452     return NULL;
453
454   /* Parse the payload */
455   ret = silc_buffer_unformat(&buffer, 
456                              SILC_STR_UI16_NSTRING_ALLOC(&newp->hostname,
457                                                          &newp->hostname_len),
458                              SILC_STR_UI_INT(&newp->port),
459                              SILC_STR_END);
460   if (ret == -1) {
461     silc_free(newp);
462     return NULL;
463   }
464
465   return newp;
466 }
467
468 /* Encodes the Key Agreement protocol and returns the encoded buffer */
469
470 SilcBuffer silc_key_agreement_payload_encode(const char *hostname,
471                                              SilcUInt32 port)
472 {
473   SilcBuffer buffer;
474   SilcUInt32 len = hostname ? strlen(hostname) : 0;
475
476   SILC_LOG_DEBUG(("Encoding Key Agreement Payload"));
477
478   buffer = silc_buffer_alloc_size(2 + len + 4);
479   if (!buffer)
480     return NULL;
481   silc_buffer_format(buffer,
482                      SILC_STR_UI_SHORT(len),
483                      SILC_STR_UI_XNSTRING(hostname, len),
484                      SILC_STR_UI_INT(port),
485                      SILC_STR_END);
486
487   return buffer;
488 }
489
490 /* Frees the Key Agreement protocol */
491
492 void silc_key_agreement_payload_free(SilcKeyAgreementPayload payload)
493 {
494   if (payload) {
495     silc_free(payload->hostname);
496     silc_free(payload);
497   }
498 }
499
500 /* Returns the hostname in the payload */
501
502 char *silc_key_agreement_get_hostname(SilcKeyAgreementPayload payload)
503 {
504   return payload->hostname;
505 }
506
507 /* Returns the port in the payload */
508
509 SilcUInt32 silc_key_agreement_get_port(SilcKeyAgreementPayload payload)
510 {
511   return payload->port;
512 }