]> jfr.im git - irc/rqf/shadowircd.git/blame - modules/m_challenge.c
Update TODO
[irc/rqf/shadowircd.git] / modules / m_challenge.c
CommitLineData
212380e3 1/*
2 * ircd-ratbox: A slightly useful ircd.
3 * m_challenge.c: Allows an IRC Operator to securely authenticate.
4 *
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2002-2005 ircd-ratbox development team
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 * USA
23 *
5366977b 24 * $Id: m_challenge.c 3161 2007-01-25 07:23:01Z nenolod $
212380e3 25 */
26
27#include "stdinc.h"
28
29#ifdef HAVE_LIBCRYPTO
30#include <openssl/pem.h>
31#include <openssl/rand.h>
32#include <openssl/rsa.h>
33#include <openssl/md5.h>
34#include <openssl/bn.h>
35#include <openssl/evp.h>
36#include <openssl/err.h>
37#endif
38
212380e3 39#include "client.h"
40#include "ircd.h"
41#include "modules.h"
42#include "numeric.h"
43#include "send.h"
44#include "s_conf.h"
45#include "msg.h"
46#include "parse.h"
13ae2f4b 47#include "match.h"
d3455e2c 48#include "logger.h"
212380e3 49#include "s_user.h"
50#include "cache.h"
51#include "s_newconf.h"
52
53#define CHALLENGE_WIDTH BUFSIZE - (NICKLEN + HOSTLEN + 12)
54#define CHALLENGE_EXPIRES 180 /* 180 seconds should be more than long enough */
55#define CHALLENGE_SECRET_LENGTH 128 /* how long our challenge secret should be */
56
57#ifndef HAVE_LIBCRYPTO
58/* Maybe this should be an error or something?-davidt */
59/* now it is -larne */
60static int challenge_load(void)
61{
62#ifndef STATIC_MODULES
63 sendto_realops_snomask(SNO_GENERAL, L_ALL,
64 "Challenge module not loaded because OpenSSL is not available.");
65 ilog(L_MAIN, "Challenge module not loaded because OpenSSL is not available.");
66 return -1;
67#else
68 return 0;
69#endif
70}
71
5366977b 72DECLARE_MODULE_AV1(challenge, challenge_load, NULL, NULL, NULL, NULL, "$Revision: 3161 $");
212380e3 73#else
74
75static int m_challenge(struct Client *, struct Client *, int, const char **);
76
77/* We have openssl support, so include /CHALLENGE */
78struct Message challenge_msgtab = {
79 "CHALLENGE", 0, 0, 0, MFLG_SLOW,
80 {mg_unreg, {m_challenge, 2}, mg_ignore, mg_ignore, mg_ignore, {m_challenge, 2}}
81};
82
83mapi_clist_av1 challenge_clist[] = { &challenge_msgtab, NULL };
5366977b 84DECLARE_MODULE_AV1(challenge, NULL, NULL, challenge_clist, NULL, NULL, "$Revision: 3161 $");
212380e3 85
86static int generate_challenge(char **r_challenge, char **r_response, RSA * key);
87
88static void
89cleanup_challenge(struct Client *target_p)
90{
91 if(target_p->localClient == NULL)
92 return;
93
90a3c35b
VY
94 rb_free(target_p->localClient->challenge);
95 rb_free(target_p->localClient->opername);
212380e3 96 target_p->localClient->challenge = NULL;
97 target_p->localClient->opername = NULL;
98 target_p->localClient->chal_time = 0;
99}
100
101/*
102 * m_challenge - generate RSA challenge for wouldbe oper
103 * parv[0] = sender prefix
104 * parv[1] = operator to challenge for, or +response
105 *
106 */
107static int
108m_challenge(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
109{
110 struct oper_conf *oper_p;
111 char *challenge = NULL; /* to placate gcc */
112 char chal_line[CHALLENGE_WIDTH];
113 unsigned char *b_response;
114 size_t cnt;
115 int len = 0;
116
117 /* if theyre an oper, reprint oper motd and ignore */
118 if(IsOper(source_p))
119 {
120 sendto_one(source_p, form_str(RPL_YOUREOPER), me.name, source_p->name);
121 send_oper_motd(source_p);
122 return 0;
123 }
124
125 if(*parv[1] == '+')
126 {
127 /* Ignore it if we aren't expecting this... -A1kmm */
128 if(!source_p->localClient->challenge)
129 return 0;
130
9f6bbe3c 131 if((rb_current_time() - source_p->localClient->chal_time) > CHALLENGE_EXPIRES)
212380e3 132 {
133 sendto_one(source_p, form_str(ERR_PASSWDMISMATCH), me.name, source_p->name);
134 ilog(L_FOPER, "EXPIRED CHALLENGE (%s) by (%s!%s@%s) (%s)",
135 source_p->localClient->opername, source_p->name,
136 source_p->username, source_p->host, source_p->sockhost);
137
138 if(ConfigFileEntry.failed_oper_notice)
139 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
140 "Expired CHALLENGE attempt by %s (%s@%s)",
141 source_p->name, source_p->username,
142 source_p->host);
143 cleanup_challenge(source_p);
144 return 0;
145 }
146
b798359b 147 b_response = rb_base64_decode((const unsigned char *)++parv[1], strlen(parv[1]), &len);
212380e3 148
149 if(len != SHA_DIGEST_LENGTH ||
150 memcmp(source_p->localClient->challenge, b_response, SHA_DIGEST_LENGTH))
151 {
152 sendto_one(source_p, form_str(ERR_PASSWDMISMATCH), me.name, source_p->name);
153 ilog(L_FOPER, "FAILED CHALLENGE (%s) by (%s!%s@%s) (%s)",
154 source_p->localClient->opername, source_p->name,
155 source_p->username, source_p->host, source_p->sockhost);
156
157 if(ConfigFileEntry.failed_oper_notice)
158 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
159 "Failed CHALLENGE attempt by %s (%s@%s)",
160 source_p->name, source_p->username,
161 source_p->host);
162
90a3c35b 163 rb_free(b_response);
212380e3 164 cleanup_challenge(source_p);
165 return 0;
166 }
167
90a3c35b 168 rb_free(b_response);
212380e3 169
170 oper_p = find_oper_conf(source_p->username, source_p->orighost,
171 source_p->sockhost,
172 source_p->localClient->opername);
173
174 if(oper_p == NULL)
175 {
176 sendto_one(source_p, form_str(ERR_NOOPERHOST),
177 me.name, source_p->name);
178 ilog(L_FOPER, "FAILED OPER (%s) by (%s!%s@%s) (%s)",
179 source_p->localClient->opername, source_p->name,
180 source_p->username, source_p->host,
181 source_p->sockhost);
182
183 if(ConfigFileEntry.failed_oper_notice)
184 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
185 "Failed CHALLENGE attempt - host mismatch by %s (%s@%s)",
186 source_p->name, source_p->username,
187 source_p->host);
188 return 0;
189 }
190
191 cleanup_challenge(source_p);
192
193 oper_up(source_p, oper_p);
194
195 ilog(L_OPERED, "OPER %s by %s!%s@%s (%s)",
196 source_p->localClient->opername, source_p->name,
197 source_p->username, source_p->host, source_p->sockhost);
198 return 0;
199 }
200
201 cleanup_challenge(source_p);
202
203 oper_p = find_oper_conf(source_p->username, source_p->orighost,
204 source_p->sockhost, parv[1]);
205
206 if(oper_p == NULL)
207 {
208 sendto_one(source_p, form_str(ERR_NOOPERHOST), me.name, source_p->name);
209 ilog(L_FOPER, "FAILED OPER (%s) by (%s!%s@%s) (%s)",
210 parv[1], source_p->name,
211 source_p->username, source_p->host, source_p->sockhost);
212
213 if(ConfigFileEntry.failed_oper_notice)
214 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
215 "Failed CHALLENGE attempt - host mismatch by %s (%s@%s)",
216 source_p->name, source_p->username, source_p->host);
217 return 0;
218 }
219
220 if(!oper_p->rsa_pubkey)
221 {
5366977b 222 sendto_one_notice(source_p, ":I'm sorry, PK authentication is not enabled for your oper{} block.");
212380e3 223 return 0;
224 }
225
663bbb28
JT
226 if(IsOperConfNeedSSL(oper_p) && !IsSSLClient(source_p))
227 {
228 sendto_one(source_p, form_str(ERR_NOOPERHOST), me.name, source_p->name);
229 ilog(L_FOPER, "FAILED CHALLENGE (%s) by (%s!%s@%s) (%s) -- requires SSL/TLS",
230 parv[1], source_p->name, source_p->username, source_p->host,
231 source_p->sockhost);
232
233 if(ConfigFileEntry.failed_oper_notice)
234 {
235 sendto_realops_snomask(SNO_GENERAL, L_ALL,
236 "Failed CHALLENGE attempt - missing SSL/TLS by %s (%s@%s)",
237 source_p->name, source_p->username, source_p->host);
238 }
239 return 0;
240 }
241
212380e3 242 if(!generate_challenge(&challenge, &(source_p->localClient->challenge), oper_p->rsa_pubkey))
243 {
244 char *chal = challenge;
9f6bbe3c 245 source_p->localClient->chal_time = rb_current_time();
212380e3 246 for(;;)
247 {
907468c4 248 cnt = rb_strlcpy(chal_line, chal, CHALLENGE_WIDTH);
212380e3 249 sendto_one(source_p, form_str(RPL_RSACHALLENGE2), me.name, source_p->name, chal_line);
250 if(cnt > CHALLENGE_WIDTH)
251 chal += CHALLENGE_WIDTH - 1;
252 else
253 break;
254
255 }
256 sendto_one(source_p, form_str(RPL_ENDOFRSACHALLENGE2),
257 me.name, source_p->name);
90a3c35b 258 rb_free(challenge);
8e43b0b4 259 source_p->localClient->opername = rb_strdup(oper_p->name);
212380e3 260 }
261 else
262 sendto_one_notice(source_p, ":Failed to generate challenge.");
263
264 return 0;
265}
266
212380e3 267static int
268generate_challenge(char **r_challenge, char **r_response, RSA * rsa)
269{
270 SHA_CTX ctx;
271 unsigned char secret[CHALLENGE_SECRET_LENGTH], *tmp;
272 unsigned long length;
273 unsigned long e = 0;
274 unsigned long cnt = 0;
275 int ret;
276
277 if(!rsa)
278 return -1;
26673969 279 if(rb_get_random(secret, CHALLENGE_SECRET_LENGTH))
212380e3 280 {
281 SHA1_Init(&ctx);
26673969
VY
282 SHA1_Update(&ctx, (uint8_t *)secret, CHALLENGE_SECRET_LENGTH);
283 *r_response = malloc(SHA_DIGEST_LENGTH);
284 SHA1_Final((uint8_t *)*r_response, &ctx);
212380e3 285
286 length = RSA_size(rsa);
8e43b0b4 287 tmp = rb_malloc(length);
212380e3 288 ret = RSA_public_encrypt(CHALLENGE_SECRET_LENGTH, secret, tmp, rsa, RSA_PKCS1_OAEP_PADDING);
289
26673969 290 if(ret >= 0)
212380e3 291 {
26673969 292 *r_challenge = (char *)rb_base64_encode(tmp, ret);
90a3c35b 293 rb_free(tmp);
212380e3 294 return 0;
295 }
26673969 296
90a3c35b
VY
297 rb_free(tmp);
298 rb_free(*r_response);
212380e3 299 *r_response = NULL;
300 }
301
302 ERR_load_crypto_strings();
303 while ((cnt < 100) && (e = ERR_get_error()))
304 {
305 ilog(L_MAIN, "SSL error: %s", ERR_error_string(e, 0));
306 cnt++;
307 }
308
309 return (-1);
310}
311
312#endif /* HAVE_LIBCRYPTO */