ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid-7.3/modules/m_cryptlink.c
Revision: 1028
Committed: Sun Nov 8 13:03:38 2009 UTC (14 years, 4 months ago) by michael
Content type: text/x-csrc
Original Path: ircd-hybrid/modules/m_cryptlink.c
File size: 14144 byte(s)
Log Message:
- move ircd-hybrid-7.2 to trunk

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 michael 912 #ifdef HAVE_LIBCRYPTO
34    
35 michael 1011 #include "list.h"
36 adx 30 #include "handlers.h"
37     #include "client.h" /* client struct */
38     #include "ircd.h" /* me */
39     #include "modules.h"
40     #include "numeric.h" /* ERR_xxx */
41     #include "send.h" /* sendto_one */
42     #include <openssl/rsa.h> /* rsa.h is implicit when building this */
43     #include "rsa.h"
44     #include "msg.h"
45     #include "parse.h"
46     #include "irc_string.h" /* strncpy_irc */
47     #include "memory.h"
48     #include "common.h" /* TRUE bleah */
49     #include "event.h"
50     #include "hash.h" /* add_to_client_hash_table */
51     #include "s_conf.h" /* struct AccessItem */
52     #include "s_log.h" /* log level defines */
53     #include "s_serv.h" /* server_estab, check_server, my_name_for_link */
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 michael 946 {mr_cryptlink, m_ignore, rfc1459_command_send_error, m_ignore, m_ignore, m_ignore}
68 adx 30 };
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 michael 948 { NULL, NULL, }
83 adx 30 };
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     conf = find_conf_name(&client_p->localClient->confs,
357     name, SERVER_TYPE);
358     if (conf == NULL)
359     {
360     cryptlink_error(client_p, "AUTH",
361     "Lost C-line for server",
362     "Lost C-line" );
363     return;
364     }
365    
366     /*
367     * if we are connecting (Handshake), we already have the name from the
368     * connect {} block in client_p->name
369     */
370     strlcpy(client_p->name, name, sizeof(client_p->name));
371    
372     p = info;
373    
374     if (!strncmp(info, "(H)", 3))
375     {
376     SetHidden(client_p);
377    
378     if ((p = strchr(info, ' ')) != NULL)
379     {
380     p++;
381     if (*p == '\0')
382     p = "(Unknown Location)";
383     }
384     else
385     p = "(Unknown Location)";
386     }
387    
388     strlcpy(client_p->info, p, sizeof(client_p->info));
389     client_p->hopcount = 0;
390    
391     aconf = (struct AccessItem *)map_to_conf(conf);
392    
393     if (!(client_p->localClient->out_cipher ||
394     (client_p->localClient->out_cipher = check_cipher(client_p, aconf))))
395     {
396     cryptlink_error(client_p, "AUTH",
397     "Couldn't find compatible cipher",
398     "Couldn't find compatible cipher");
399     return;
400     }
401    
402     encrypted = MyMalloc(RSA_size(ServerInfo.rsa_private_key));
403     enc_len = RSA_public_encrypt(client_p->localClient->out_cipher->keylen,
404     (unsigned char *)key,
405     (unsigned char *)encrypted,
406     aconf->rsa_public_key,
407     RSA_PKCS1_PADDING);
408    
409     if (enc_len <= 0)
410     {
411     report_crypto_errors();
412     MyFree(encrypted);
413     cryptlink_error(client_p, "AUTH",
414     "Couldn't encrypt data",
415     "Couldn't encrypt data");
416     return;
417     }
418    
419     base64_block(&b64_key, encrypted, enc_len);
420    
421     MyFree(encrypted);
422    
423     if (!IsWaitAuth(client_p))
424     cryptlink_init(client_p, conf, NULL);
425    
426     sendto_one(client_p, "CRYPTLINK AUTH %s %s",
427     client_p->localClient->out_cipher->name,
428     b64_key);
429    
430     /* needed for old servers that can't shove data back into slink */
431     send_queued_write(client_p);
432    
433     SetCryptOut(client_p);
434     MyFree(b64_key);
435     }
436    
437     /* parse_cryptserv_args()
438     *
439     * inputs - parv parameters
440     * - parc count
441     * - info string (to be filled in by this routine)
442     * - key (to be filled in by this routine)
443     * output - NULL if invalid params, server name otherwise
444     * side effects - parv[2] is trimmed to HOSTLEN size if needed.
445     */
446     static char *
447     parse_cryptserv_args(struct Client *client_p, char *parv[],
448     int parc, char *info, char *key)
449     {
450     char *name;
451     unsigned char *tmp, *out;
452     int len;
453     int decoded_len;
454    
455     info[0] = '\0';
456    
457     name = parv[2];
458    
459     /* parv[2] contains encrypted auth data */
460     if (!(decoded_len = unbase64_block(&tmp, parv[3],
461     strlen(parv[3]))))
462     {
463     cryptlink_error(client_p, "SERV",
464     "Couldn't base64 decode data",
465     NULL);
466     return(NULL);
467     }
468    
469     if (verify_private_key() == -1)
470     {
471     sendto_realops_flags(UMODE_ALL, L_ADMIN,
472     "verify_private_key() returned -1. Check log for information.");
473     }
474    
475     if (ServerInfo.rsa_private_key == NULL)
476     {
477     cryptlink_error(client_p, "SERV", "No local private key found", NULL);
478     return(NULL);
479     }
480    
481     out = MyMalloc(RSA_size(ServerInfo.rsa_private_key));
482     len = RSA_private_decrypt(decoded_len, tmp, out,
483     ServerInfo.rsa_private_key,
484     RSA_PKCS1_PADDING);
485    
486     MyFree(tmp);
487    
488     if (len < CIPHERKEYLEN)
489     {
490     report_crypto_errors();
491     if (len < 0)
492     {
493     cryptlink_error(client_p, "AUTH", "Decryption failed", NULL);
494     }
495     else
496     {
497     cryptlink_error(client_p, "AUTH", "Not enough random data sent", NULL);
498     }
499     MyFree(out);
500     return(NULL);
501     }
502    
503     memcpy(key, out, CIPHERKEYLEN);
504     MyFree(out);
505    
506     strlcpy(info, parv[4], REALLEN + 1);
507    
508     if (strlen(name) > HOSTLEN)
509     name[HOSTLEN] = '\0';
510    
511     return(name);
512     }
513    
514     /* bogus_host()
515     *
516     * inputs - hostname
517     * output - 1 if a bogus hostname input, 0 if its valid
518     * side effects - none
519     */
520     static int
521     bogus_host(char *host)
522     {
523     unsigned int length = 0;
524     unsigned int dots = 0;
525     char *s = host;
526    
527     for (; *s; s++)
528     {
529     if (!IsServChar(*s))
530     return(1);
531    
532     ++length;
533    
534     if ('.' == *s)
535     ++dots;
536     }
537    
538     return(!dots || length > HOSTLEN);
539     }
540 michael 912
541     #endif

Properties

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