ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/modules/m_cryptlink.c
Revision: 139
Committed: Sun Oct 16 06:01:13 2005 UTC (20 years, 9 months ago) by db
Content type: text/x-csrc
File size: 14781 byte(s)
Log Message:
- get rid of map_conf_item and unmap_conf_item
- Use an union in struct ConfItem, but only allocate memory needed


File Contents

# User Rev Content
1 adx 30 /*
2     * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
3     * m_cryptlink.c: Used to negotiate an encrypted link.
4     *
5     * Copyright (C) 2002 by the past and present ircd coders, and others.
6     *
7     * This program is free software; you can redistribute it and/or modify
8     * it under the terms of the GNU General Public License as published by
9     * the Free Software Foundation; either version 2 of the License, or
10     * (at your option) any later version.
11     *
12     * This program is distributed in the hope that it will be useful,
13     * but WITHOUT ANY WARRANTY; without even the implied warranty of
14     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15     * GNU General Public License for more details.
16     *
17     * You should have received a copy of the GNU General Public License
18     * along with this program; if not, write to the Free Software
19     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20     * USA
21     *
22 knight 31 * $Id$
23 adx 30 */
24    
25     /*
26     * CRYPTLINK protocol.
27     *
28     * Please see doc/cryptlink.txt for a description of this protocol.
29     *
30     */
31    
32     #include "stdinc.h"
33     #include "handlers.h"
34     #include "client.h" /* client struct */
35     #include "ircd.h" /* me */
36     #include "modules.h"
37     #include "numeric.h" /* ERR_xxx */
38     #include "send.h" /* sendto_one */
39     #include <openssl/rsa.h> /* rsa.h is implicit when building this */
40     #include "rsa.h"
41     #include "msg.h"
42     #include "parse.h"
43     #include "common.h" /* TRUE bleah */
44     #include "hash.h" /* add_to_client_hash_table */
45     #include "s_conf.h" /* struct AccessItem */
46     #include "s_serv.h" /* server_estab, check_server, my_name_for_link */
47     #include "s_stats.h" /* ServerStats */
48     #include "motd.h"
49    
50     static int bogus_host(char *host);
51     static char *parse_cryptserv_args(struct Client *client_p,
52     char *parv[], int parc, char *info,
53     char *key);
54    
55     static void mr_cryptlink(struct Client *, struct Client *, int, char **);
56     static void cryptlink_serv(struct Client *, struct Client *, int, char **);
57     static void cryptlink_auth(struct Client *, struct Client *, int, char **);
58    
59     struct Message cryptlink_msgtab = {
60     "CRYPTLINK", 0, 0, 4, 0, MFLG_SLOW | MFLG_UNREG, 0,
61     {mr_cryptlink, m_ignore, m_error, m_ignore, m_ignore, m_ignore}
62     };
63    
64     struct CryptLinkStruct
65     {
66     const char *cmd; /* CRYPTLINK <command> to match */
67     void (*handler)(); /* Function to call */
68     };
69    
70     static struct CryptLinkStruct cryptlink_cmd_table[] =
71     {
72     /* command function */
73     { "AUTH", cryptlink_auth, },
74     { "SERV", cryptlink_serv, },
75     /* End of table */
76     { (char *)0, (void (*)())0, }
77     };
78    
79     #ifndef STATIC_MODULES
80     void
81     _modinit(void)
82     {
83     mod_add_cmd(&cryptlink_msgtab);
84     }
85    
86     void
87     _moddeinit(void)
88     {
89     mod_del_cmd(&cryptlink_msgtab);
90     }
91    
92 knight 31 const char *_version = "$Revision$";
93 adx 30 #endif
94    
95    
96     /* mr_cryptlink - CRYPTLINK message handler
97     * parv[0] == CRYPTLINK
98     * parv[1] = command (SERV, AUTH)
99     * parv[2] = Parameters specific to each command (parv[1]):
100     * SERV - parc must be >= 5
101     * parv[0] == CRYPTLINK
102     * parv[1] == SERV
103     * parv[2] == server name
104     * parv[3] == keyphrase
105     * parv[4] == :server info (M-line)
106     * AUTH - parc must be >= 4
107     * parv[0] == CRYPTLINK
108     * parv[1] == AUTH
109     * parv[2] == cipher (eg. BF/168)
110     * parv[3] == keyphrase
111     */
112     static void
113     mr_cryptlink(struct Client *client_p, struct Client *source_p,
114     int parc, char *parv[])
115     {
116     int i;
117    
118     for (i = 0; cryptlink_cmd_table[i].handler; i++)
119     {
120     /* Traverse through the command table */
121     if (!irccmp(cryptlink_cmd_table[i].cmd, parv[1]))
122     {
123     /*
124     * Match found. Time to execute the function
125     */
126     cryptlink_cmd_table[i].handler(client_p, source_p, parc, parv);
127     }
128     }
129     }
130    
131     /*
132     * cryptlink_auth - CRYPTLINK AUTH message handler
133     * parv[1] = secret key
134     */
135     static void
136     cryptlink_auth(struct Client *client_p, struct Client *source_p,
137     int parc, char *parv[])
138     {
139     struct EncCapability *ecap;
140     struct ConfItem *conf;
141     struct AccessItem *aconf;
142     int enc_len;
143     int len;
144     unsigned char *enc;
145     unsigned char *key;
146    
147     if (parc < 4)
148     {
149     cryptlink_error(client_p, "AUTH", "Invalid params",
150     "CRYPTLINK AUTH - Invalid params");
151     return;
152     }
153    
154     if (!IsWaitAuth(client_p))
155     return;
156    
157     for (ecap = CipherTable; ecap->name; ecap++)
158     {
159     if ((!irccmp(ecap->name, parv[2])) &&
160     (IsCapableEnc(client_p, ecap->cap)))
161     {
162     client_p->localClient->in_cipher = ecap;
163     break;
164     }
165     }
166    
167     if (client_p->localClient->in_cipher == NULL)
168     {
169     cryptlink_error(client_p, "AUTH", "Invalid cipher", "Invalid cipher");
170     return;
171     }
172    
173     if (!(enc_len = unbase64_block(&enc, parv[3], strlen(parv[3]))))
174     {
175     cryptlink_error(client_p, "AUTH",
176     "Could not base64 decode response",
177     "Malformed CRYPTLINK AUTH reply");
178     return;
179     }
180    
181     if (verify_private_key() == -1)
182     {
183     sendto_realops_flags(UMODE_ALL, L_ADMIN,
184     "verify_private_key() returned -1. Check log for information.");
185     }
186    
187     key = MyMalloc(RSA_size(ServerInfo.rsa_private_key));
188     len = RSA_private_decrypt(enc_len, (unsigned char *)enc,(unsigned char *)key,
189     ServerInfo.rsa_private_key,
190     RSA_PKCS1_PADDING);
191    
192     if (len < client_p->localClient->in_cipher->keylen)
193     {
194     report_crypto_errors();
195     if (len < 0)
196     {
197     cryptlink_error(client_p, "AUTH",
198     "Decryption failed",
199     "Malformed CRYPTLINK AUTH reply");
200     }
201     else
202     {
203     cryptlink_error(client_p, "AUTH",
204     "Not enough random data sent",
205     "Malformed CRYPTLINK AUTH reply");
206     }
207     MyFree(enc);
208     MyFree(key);
209     return;
210     }
211    
212     if (memcmp(key, client_p->localClient->in_key,
213     client_p->localClient->in_cipher->keylen) != 0)
214     {
215     cryptlink_error(client_p, "AUTH",
216     "Unauthorized server connection attempt",
217     "Malformed CRYPTLINK AUTH reply");
218     return;
219     }
220    
221 db 126 conf = client_p->serv->sconf;
222 adx 30
223     if (conf == NULL)
224     {
225     cryptlink_error(client_p, "AUTH",
226 db 126 "Lost connect block for server",
227     "Lost connect block");
228 adx 30 return;
229     }
230 db 139 aconf = &conf->conf.AccessItem;
231 adx 30
232     if (!(client_p->localClient->out_cipher ||
233     (client_p->localClient->out_cipher = check_cipher(client_p, aconf))))
234     {
235     cryptlink_error(client_p, "AUTH",
236     "Couldn't find compatible cipher",
237     "Couldn't find compatible cipher");
238     return;
239     }
240    
241     /* set hopcount */
242     client_p->hopcount = 1;
243    
244     SetCryptIn(client_p);
245     ClearWaitAuth(client_p);
246     server_estab(client_p);
247     }
248    
249     /*
250     * cryptlink_serv - CRYPTLINK SERV message handler
251     * parv[0] == CRYPTLINK
252     * parv[1] == SERV
253     * parv[2] == server name
254     * parv[3] == keyphrase
255     * parv[4] == :server info (M-line)
256     */
257     static void
258     cryptlink_serv(struct Client *client_p, struct Client *source_p,
259     int parc, char *parv[])
260     {
261     char info[REALLEN + 1];
262     char *name;
263     struct Client *target_p;
264     char *key = client_p->localClient->out_key;
265     unsigned char *b64_key;
266     struct ConfItem *conf;
267     struct AccessItem *aconf;
268     char *encrypted;
269     const char *p;
270     int enc_len;
271    
272     /*
273     if (client_p->name[0] != 0)
274     return;
275     */
276    
277     if ((parc < 5) || (*parv[4] == '\0'))
278     {
279     cryptlink_error(client_p, "SERV", "Invalid params",
280     "CRYPTLINK SERV - Invalid params");
281     return;
282     }
283    
284     if ((name = parse_cryptserv_args(client_p, parv, parc, info, key)) == NULL)
285     {
286     cryptlink_error(client_p, "SERV", "Invalid params",
287     "CRYPTLINK SERV - Invalid params");
288     return;
289     }
290    
291     /* CRYPTLINK SERV support => TS support */
292     client_p->tsinfo = TS_DOESTS;
293    
294     if (bogus_host(name))
295     {
296     exit_client(client_p, client_p, "Bogus server name");
297     return;
298     }
299    
300     /* Now we just have to call check_server and everything should be
301     * checked for us... -A1kmm. */
302     switch (check_server(name, client_p, CHECK_SERVER_CRYPTLINK))
303     {
304     case -1:
305     if (ConfigFileEntry.warn_no_nline)
306     {
307     cryptlink_error(client_p, "SERV",
308     "Unauthorized server connection attempt: No entry for server",
309     NULL);
310     }
311     exit_client(client_p, client_p, "Invalid server name");
312     return;
313     break;
314     case -2:
315     cryptlink_error(client_p, "SERV",
316     "Unauthorized server connection attempt: CRYPTLINK not "
317     "enabled on remote server",
318     "CRYPTLINK not enabled");
319     return;
320     break;
321     case -3:
322     cryptlink_error(client_p, "SERV",
323     "Unauthorized server connection attempt: Invalid host",
324     "Invalid host");
325     return;
326     break;
327     }
328    
329     if ((target_p = find_server(name)))
330     {
331     /*
332     * This link is trying feed me a server that I already have
333     * access through another path -- multiple paths not accepted
334     * currently, kill this link immediately!!
335     *
336     * Rather than KILL the link which introduced it, KILL the
337     * youngest of the two links. -avalon
338     *
339     * Definitely don't do that here. This is from an unregistered
340     * connect - A1kmm.
341     */
342     cryptlink_error(client_p, "SERV",
343     "Attempt to re-introduce existing server",
344     "Server Exists");
345     return;
346     }
347    
348     if (ServerInfo.hub && IsCapable(client_p, CAP_LL))
349     {
350     if (IsCapable(client_p, CAP_HUB))
351     {
352     ClearCap(client_p,CAP_LL);
353     sendto_realops_flags(UMODE_ALL, L_ALL,
354     "*** LazyLinks to a hub from a hub, that's a no-no.");
355     }
356     else
357     {
358     client_p->localClient->serverMask = nextFreeMask();
359    
360     if(!client_p->localClient->serverMask)
361     {
362     sendto_realops_flags(UMODE_ALL, L_ALL,
363     "serverMask is full!");
364     /* try and negotiate a non LL connect */
365     ClearCap(client_p,CAP_LL);
366     }
367     }
368     }
369     else if (IsCapable(client_p, CAP_LL))
370     {
371     if (!IsCapable(client_p, CAP_HUB))
372     {
373     ClearCap(client_p,CAP_LL);
374     sendto_realops_flags(UMODE_ALL, L_ALL,
375     "*** LazyLinks to a leaf from a leaf, that's a no-no.");
376     }
377     }
378    
379 db 126 conf = client_p->serv->sconf;
380 db 101
381 adx 30 if (conf == NULL)
382     {
383     cryptlink_error(client_p, "AUTH",
384 db 126 "Lost connect block for server",
385     "Lost connect block" );
386 adx 30 return;
387     }
388 db 139 aconf = &conf->conf.AccessItem;
389 adx 30
390     /*
391     * if we are connecting (Handshake), we already have the name from the
392     * connect {} block in client_p->name
393     */
394     strlcpy(client_p->name, name, sizeof(client_p->name));
395    
396     p = info;
397    
398     if (!strncmp(info, "(H)", 3))
399     {
400     SetHidden(client_p);
401    
402     if ((p = strchr(info, ' ')) != NULL)
403     {
404     p++;
405     if (*p == '\0')
406     p = "(Unknown Location)";
407     }
408     else
409     p = "(Unknown Location)";
410     }
411    
412     strlcpy(client_p->info, p, sizeof(client_p->info));
413     client_p->hopcount = 0;
414    
415     if (!(client_p->localClient->out_cipher ||
416     (client_p->localClient->out_cipher = check_cipher(client_p, aconf))))
417     {
418     cryptlink_error(client_p, "AUTH",
419     "Couldn't find compatible cipher",
420     "Couldn't find compatible cipher");
421     return;
422     }
423    
424     encrypted = MyMalloc(RSA_size(ServerInfo.rsa_private_key));
425     enc_len = RSA_public_encrypt(client_p->localClient->out_cipher->keylen,
426     (unsigned char *)key,
427     (unsigned char *)encrypted,
428     aconf->rsa_public_key,
429     RSA_PKCS1_PADDING);
430    
431     if (enc_len <= 0)
432     {
433     report_crypto_errors();
434     MyFree(encrypted);
435     cryptlink_error(client_p, "AUTH",
436     "Couldn't encrypt data",
437     "Couldn't encrypt data");
438     return;
439     }
440    
441     base64_block(&b64_key, encrypted, enc_len);
442    
443     MyFree(encrypted);
444    
445     if (!IsWaitAuth(client_p))
446     {
447 db 126 cryptlink_init(client_p, aconf, NULL);
448 adx 30 }
449    
450     sendto_one(client_p, "CRYPTLINK AUTH %s %s",
451     client_p->localClient->out_cipher->name,
452     b64_key);
453    
454     /* needed for old servers that can't shove data back into slink */
455     send_queued_write(client_p);
456    
457     SetCryptOut(client_p);
458     MyFree(b64_key);
459     }
460    
461     /* parse_cryptserv_args()
462     *
463     * inputs - parv parameters
464     * - parc count
465     * - info string (to be filled in by this routine)
466     * - key (to be filled in by this routine)
467     * output - NULL if invalid params, server name otherwise
468     * side effects - parv[2] is trimmed to HOSTLEN size if needed.
469     */
470     static char *
471     parse_cryptserv_args(struct Client *client_p, char *parv[],
472     int parc, char *info, char *key)
473     {
474     char *name;
475     unsigned char *tmp, *out;
476     int len;
477     int decoded_len;
478    
479     info[0] = '\0';
480    
481     name = parv[2];
482    
483     /* parv[2] contains encrypted auth data */
484     if (!(decoded_len = unbase64_block(&tmp, parv[3],
485     strlen(parv[3]))))
486     {
487     cryptlink_error(client_p, "SERV",
488     "Couldn't base64 decode data",
489     NULL);
490     return(NULL);
491     }
492    
493     if (verify_private_key() == -1)
494     {
495     sendto_realops_flags(UMODE_ALL, L_ADMIN,
496     "verify_private_key() returned -1. Check log for information.");
497     }
498    
499     if (ServerInfo.rsa_private_key == NULL)
500     {
501     cryptlink_error(client_p, "SERV", "No local private key found", NULL);
502     return(NULL);
503     }
504    
505     out = MyMalloc(RSA_size(ServerInfo.rsa_private_key));
506     len = RSA_private_decrypt(decoded_len, tmp, out,
507     ServerInfo.rsa_private_key,
508     RSA_PKCS1_PADDING);
509    
510     MyFree(tmp);
511    
512     if (len < CIPHERKEYLEN)
513     {
514     report_crypto_errors();
515     if (len < 0)
516     {
517     cryptlink_error(client_p, "AUTH", "Decryption failed", NULL);
518     }
519     else
520     {
521     cryptlink_error(client_p, "AUTH", "Not enough random data sent", NULL);
522     }
523     MyFree(out);
524     return(NULL);
525     }
526    
527     memcpy(key, out, CIPHERKEYLEN);
528     MyFree(out);
529    
530     strlcpy(info, parv[4], REALLEN + 1);
531    
532     if (strlen(name) > HOSTLEN)
533     name[HOSTLEN] = '\0';
534    
535     return(name);
536     }
537    
538     /* bogus_host()
539     *
540     * inputs - hostname
541     * output - 1 if a bogus hostname input, 0 if its valid
542     * side effects - none
543     */
544     static int
545     bogus_host(char *host)
546     {
547     unsigned int length = 0;
548     unsigned int dots = 0;
549     char *s = host;
550    
551     for (; *s; s++)
552     {
553     if (!IsServChar(*s))
554     return(1);
555    
556     ++length;
557    
558     if ('.' == *s)
559     ++dots;
560     }
561    
562     return(!dots || length > HOSTLEN);
563     }

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision