/[svn]/ircd-hybrid/modules/m_cryptlink.c
ViewVC logotype

Contents of /ircd-hybrid/modules/m_cryptlink.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 32 - (show annotations)
Sun Oct 2 20:41:23 2005 UTC (14 years, 2 months ago) by knight
File MIME type: text/x-chdr
File size: 15102 byte(s)
- svn:keywords

1 /*
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 * $Id$
23 */
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 const char *_version = "$Revision$";
99 #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 Revision

svnadmin@ircd-hybrid.org
ViewVC Help
Powered by ViewVC 1.1.26