ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/modules/m_cryptlink.c
Revision: 101
Committed: Mon Oct 10 03:52:14 2005 UTC (20 years, 10 months ago) by db
Content type: text/x-csrc
File size: 14800 byte(s)
Log Message:
- First pass at s_conf.c cleanup for attach clients
- attach_conf() is gone replaced with attach_iline()
  Clients/servers have one single I line "attached", i.e. the confs
  list is gone.
- Add attach_leaf_hub()
  Add a leaf or hub mask to the given server, ->serv must exist
  since there can be a list of hub masks and leaf masks, this continues
  to be a dlink list
- Removed redundant find_conf_exact()
  This function can be replaced in all cases with find_exact_name_conf()
  notably because all find_conf_exact did differently was count whether
  an oper conf would cause an already connected client to exceed class limits.
- oper_up() now takes an extra conf pointer pointing to the found
  oper conf, the oper flags are set in the client->iline as necessary
  in oper_up()
- Cleaned up more prototypes that should have been moved from s_conf.h
  to parse_aline.h notably. find_kill() and find_gkill()
- m_stats.c needs more cleanup since memory in attached "confs" is now
  not counted. This should be replaced with iline memory count and
  hub/leaf confs memory count.
  

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

Properties

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