ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid-7.2/modules/m_cryptlink.c
Revision: 33
Committed: Sun Oct 2 20:50:00 2005 UTC (19 years, 10 months ago) by knight
Content type: text/x-csrc
Original Path: ircd-hybrid/modules/m_cryptlink.c
File size: 15102 byte(s)
Log Message:
- svn:keywords

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

Properties

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