ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid-7.2/modules/m_cryptlink.c
Revision: 1011
Committed: Fri Sep 18 10:14:09 2009 UTC (14 years, 7 months ago) by michael
Content type: text/x-csrc
File size: 14144 byte(s)
Log Message:
- move list manipulation routines from tools.c to list.c
- mem_frob() goes to memory.c
- sort out redundant/unneeded header includes

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 #ifdef HAVE_LIBCRYPTO
34
35 #include "list.h"
36 #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 {mr_cryptlink, m_ignore, rfc1459_command_send_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 { NULL, NULL, }
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 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
541 #endif

Properties

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