ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid-8/modules/m_cryptlink.c
Revision: 1243
Committed: Fri Sep 30 10:47:53 2011 UTC (14 years, 9 months ago) by michael
Content type: text/x-csrc
File size: 13737 byte(s)
Log Message:
- move content of msg.h, ircd_handler.h and handlers.h into parse.h and
  remove headers accordingly
- killed common.h
- remove m_killhost.c and m_flags.c from contrib/
- sort out unused header includes here and there

File Contents

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

Properties

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