ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid-7.2/modules/m_cryptlink.c
Revision: 896
Committed: Sat Nov 3 08:54:09 2007 UTC (18 years, 11 months ago) by michael
Content type: text/x-csrc
File size: 14152 byte(s)
Log Message:
- Killed s_stats.c

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

Properties

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