ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/modules/m_cryptlink.c
Revision: 126
Committed: Fri Oct 14 02:41:46 2005 UTC (20 years, 10 months ago) by db
Content type: text/x-csrc
File size: 14771 byte(s)
Log Message:
- attach/conf cleanup take 2
- Each client has now one AccessItem for its connect
  stored in localClient->iline
- The corresponding class is now stored in localClient->class

The ramifications of this move are, there is no conf list to traverse
to find the AccessItem, the class is instantly available from the localClient
struct without having to traverse the confs list and indirectly through the
aconf. This speeds up get_sendq etc. functions. As a bonus, at least
4 fewer bytes are used in the Client struct, since a dlink list is 4 words.
It does mean there is no longer a separate conf oper, which leads to the
kludge of patching the clients iline into an oper conf when
a client opers up. I don't think the oper flags are used after the client
is opered, so the patching operation may not be necessary.

- Server confs are stored in ->serv->sconf as before but attaching
  happens much earlier.
- server hub/leaf masks continues to be a dlink list but linked from
  the ->serv which is only allocated for servers.

- cleaned up some comments, added a comment, notably to check_server()
  which badly needed it.
- Pass ClassItem or AccessItem etc. in when it makes more sense than passing
  in struct ConfItem. This simplified and clarified rebuild_cidr_class()

And lo, there was a great rejoicing.


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

Properties

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