ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/modules/m_cryptlink.c
Revision: 69
Committed: Tue Oct 4 16:09:51 2005 UTC (20 years, 10 months ago) by adx
Content type: text/x-csrc
File size: 14909 byte(s)
Log Message:
- splitted ircd/libio, all headers connected with libio sources have been
  moved for internal use only. To use libio interface, include "libio.h"
  (which is already done in "stdinc.h")


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     conf = find_conf_name(&client_p->localClient->confs,
222     client_p->name, SERVER_TYPE);
223    
224     if (conf == NULL)
225     {
226     cryptlink_error(client_p, "AUTH",
227     "Lost C-line for server",
228     "Lost C-line");
229     return;
230     }
231    
232     aconf = (struct AccessItem *)map_to_conf(conf);
233    
234     if (!(client_p->localClient->out_cipher ||
235     (client_p->localClient->out_cipher = check_cipher(client_p, aconf))))
236     {
237     cryptlink_error(client_p, "AUTH",
238     "Couldn't find compatible cipher",
239     "Couldn't find compatible cipher");
240     return;
241     }
242    
243     /* set hopcount */
244     client_p->hopcount = 1;
245    
246     SetCryptIn(client_p);
247     ClearWaitAuth(client_p);
248     server_estab(client_p);
249     }
250    
251     /*
252     * cryptlink_serv - CRYPTLINK SERV message handler
253     * parv[0] == CRYPTLINK
254     * parv[1] == SERV
255     * parv[2] == server name
256     * parv[3] == keyphrase
257     * parv[4] == :server info (M-line)
258     */
259     static void
260     cryptlink_serv(struct Client *client_p, struct Client *source_p,
261     int parc, char *parv[])
262     {
263     char info[REALLEN + 1];
264     char *name;
265     struct Client *target_p;
266     char *key = client_p->localClient->out_key;
267     unsigned char *b64_key;
268     struct ConfItem *conf;
269     struct AccessItem *aconf;
270     char *encrypted;
271     const char *p;
272     int enc_len;
273    
274     /*
275     if (client_p->name[0] != 0)
276     return;
277     */
278    
279     if ((parc < 5) || (*parv[4] == '\0'))
280     {
281     cryptlink_error(client_p, "SERV", "Invalid params",
282     "CRYPTLINK SERV - Invalid params");
283     return;
284     }
285    
286     if ((name = parse_cryptserv_args(client_p, parv, parc, info, key)) == NULL)
287     {
288     cryptlink_error(client_p, "SERV", "Invalid params",
289     "CRYPTLINK SERV - Invalid params");
290     return;
291     }
292    
293     /* CRYPTLINK SERV support => TS support */
294     client_p->tsinfo = TS_DOESTS;
295    
296     if (bogus_host(name))
297     {
298     exit_client(client_p, client_p, "Bogus server name");
299     return;
300     }
301    
302     /* Now we just have to call check_server and everything should be
303     * checked for us... -A1kmm. */
304     switch (check_server(name, client_p, CHECK_SERVER_CRYPTLINK))
305     {
306     case -1:
307     if (ConfigFileEntry.warn_no_nline)
308     {
309     cryptlink_error(client_p, "SERV",
310     "Unauthorized server connection attempt: No entry for server",
311     NULL);
312     }
313     exit_client(client_p, client_p, "Invalid server name");
314     return;
315     break;
316     case -2:
317     cryptlink_error(client_p, "SERV",
318     "Unauthorized server connection attempt: CRYPTLINK not "
319     "enabled on remote server",
320     "CRYPTLINK not enabled");
321     return;
322     break;
323     case -3:
324     cryptlink_error(client_p, "SERV",
325     "Unauthorized server connection attempt: Invalid host",
326     "Invalid host");
327     return;
328     break;
329     }
330    
331     if ((target_p = find_server(name)))
332     {
333     /*
334     * This link is trying feed me a server that I already have
335     * access through another path -- multiple paths not accepted
336     * currently, kill this link immediately!!
337     *
338     * Rather than KILL the link which introduced it, KILL the
339     * youngest of the two links. -avalon
340     *
341     * Definitely don't do that here. This is from an unregistered
342     * connect - A1kmm.
343     */
344     cryptlink_error(client_p, "SERV",
345     "Attempt to re-introduce existing server",
346     "Server Exists");
347     return;
348     }
349    
350     if (ServerInfo.hub && IsCapable(client_p, CAP_LL))
351     {
352     if (IsCapable(client_p, CAP_HUB))
353     {
354     ClearCap(client_p,CAP_LL);
355     sendto_realops_flags(UMODE_ALL, L_ALL,
356     "*** LazyLinks to a hub from a hub, that's a no-no.");
357     }
358     else
359     {
360     client_p->localClient->serverMask = nextFreeMask();
361    
362     if(!client_p->localClient->serverMask)
363     {
364     sendto_realops_flags(UMODE_ALL, L_ALL,
365     "serverMask is full!");
366     /* try and negotiate a non LL connect */
367     ClearCap(client_p,CAP_LL);
368     }
369     }
370     }
371     else if (IsCapable(client_p, CAP_LL))
372     {
373     if (!IsCapable(client_p, CAP_HUB))
374     {
375     ClearCap(client_p,CAP_LL);
376     sendto_realops_flags(UMODE_ALL, L_ALL,
377     "*** LazyLinks to a leaf from a leaf, that's a no-no.");
378     }
379     }
380    
381     conf = find_conf_name(&client_p->localClient->confs,
382     name, SERVER_TYPE);
383     if (conf == NULL)
384     {
385     cryptlink_error(client_p, "AUTH",
386     "Lost C-line for server",
387     "Lost C-line" );
388     return;
389     }
390    
391     /*
392     * if we are connecting (Handshake), we already have the name from the
393     * connect {} block in client_p->name
394     */
395     strlcpy(client_p->name, name, sizeof(client_p->name));
396    
397     p = info;
398    
399     if (!strncmp(info, "(H)", 3))
400     {
401     SetHidden(client_p);
402    
403     if ((p = strchr(info, ' ')) != NULL)
404     {
405     p++;
406     if (*p == '\0')
407     p = "(Unknown Location)";
408     }
409     else
410     p = "(Unknown Location)";
411     }
412    
413     strlcpy(client_p->info, p, sizeof(client_p->info));
414     client_p->hopcount = 0;
415    
416     aconf = (struct AccessItem *)map_to_conf(conf);
417    
418     if (!(client_p->localClient->out_cipher ||
419     (client_p->localClient->out_cipher = check_cipher(client_p, aconf))))
420     {
421     cryptlink_error(client_p, "AUTH",
422     "Couldn't find compatible cipher",
423     "Couldn't find compatible cipher");
424     return;
425     }
426    
427     encrypted = MyMalloc(RSA_size(ServerInfo.rsa_private_key));
428     enc_len = RSA_public_encrypt(client_p->localClient->out_cipher->keylen,
429     (unsigned char *)key,
430     (unsigned char *)encrypted,
431     aconf->rsa_public_key,
432     RSA_PKCS1_PADDING);
433    
434     if (enc_len <= 0)
435     {
436     report_crypto_errors();
437     MyFree(encrypted);
438     cryptlink_error(client_p, "AUTH",
439     "Couldn't encrypt data",
440     "Couldn't encrypt data");
441     return;
442     }
443    
444     base64_block(&b64_key, encrypted, enc_len);
445    
446     MyFree(encrypted);
447    
448     if (!IsWaitAuth(client_p))
449     {
450     cryptlink_init(client_p, conf, NULL);
451     }
452    
453     sendto_one(client_p, "CRYPTLINK AUTH %s %s",
454     client_p->localClient->out_cipher->name,
455     b64_key);
456    
457     /* needed for old servers that can't shove data back into slink */
458     send_queued_write(client_p);
459    
460     SetCryptOut(client_p);
461     MyFree(b64_key);
462     }
463    
464     /* parse_cryptserv_args()
465     *
466     * inputs - parv parameters
467     * - parc count
468     * - info string (to be filled in by this routine)
469     * - key (to be filled in by this routine)
470     * output - NULL if invalid params, server name otherwise
471     * side effects - parv[2] is trimmed to HOSTLEN size if needed.
472     */
473     static char *
474     parse_cryptserv_args(struct Client *client_p, char *parv[],
475     int parc, char *info, char *key)
476     {
477     char *name;
478     unsigned char *tmp, *out;
479     int len;
480     int decoded_len;
481    
482     info[0] = '\0';
483    
484     name = parv[2];
485    
486     /* parv[2] contains encrypted auth data */
487     if (!(decoded_len = unbase64_block(&tmp, parv[3],
488     strlen(parv[3]))))
489     {
490     cryptlink_error(client_p, "SERV",
491     "Couldn't base64 decode data",
492     NULL);
493     return(NULL);
494     }
495    
496     if (verify_private_key() == -1)
497     {
498     sendto_realops_flags(UMODE_ALL, L_ADMIN,
499     "verify_private_key() returned -1. Check log for information.");
500     }
501    
502     if (ServerInfo.rsa_private_key == NULL)
503     {
504     cryptlink_error(client_p, "SERV", "No local private key found", NULL);
505     return(NULL);
506     }
507    
508     out = MyMalloc(RSA_size(ServerInfo.rsa_private_key));
509     len = RSA_private_decrypt(decoded_len, tmp, out,
510     ServerInfo.rsa_private_key,
511     RSA_PKCS1_PADDING);
512    
513     MyFree(tmp);
514    
515     if (len < CIPHERKEYLEN)
516     {
517     report_crypto_errors();
518     if (len < 0)
519     {
520     cryptlink_error(client_p, "AUTH", "Decryption failed", NULL);
521     }
522     else
523     {
524     cryptlink_error(client_p, "AUTH", "Not enough random data sent", NULL);
525     }
526     MyFree(out);
527     return(NULL);
528     }
529    
530     memcpy(key, out, CIPHERKEYLEN);
531     MyFree(out);
532    
533     strlcpy(info, parv[4], REALLEN + 1);
534    
535     if (strlen(name) > HOSTLEN)
536     name[HOSTLEN] = '\0';
537    
538     return(name);
539     }
540    
541     /* bogus_host()
542     *
543     * inputs - hostname
544     * output - 1 if a bogus hostname input, 0 if its valid
545     * side effects - none
546     */
547     static int
548     bogus_host(char *host)
549     {
550     unsigned int length = 0;
551     unsigned int dots = 0;
552     char *s = host;
553    
554     for (; *s; s++)
555     {
556     if (!IsServChar(*s))
557     return(1);
558    
559     ++length;
560    
561     if ('.' == *s)
562     ++dots;
563     }
564    
565     return(!dots || length > HOSTLEN);
566     }

Properties

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