]> jfr.im git - solanum.git/blame - modules/m_challenge.c
extensions/invite_notify: make the NOTICE optional, configurable
[solanum.git] / modules / m_challenge.c
CommitLineData
212380e3
AC
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
212380e3
AC
23 */
24
25#include "stdinc.h"
26
27#ifdef HAVE_LIBCRYPTO
8e9a7418 28# include <openssl/err.h>
29# include <openssl/evp.h>
30# include <openssl/pem.h>
31# include <openssl/rsa.h>
32# include <openssl/sha.h>
212380e3
AC
33#endif
34
212380e3
AC
35#include "client.h"
36#include "ircd.h"
37#include "modules.h"
38#include "numeric.h"
39#include "send.h"
40#include "s_conf.h"
41#include "msg.h"
42#include "parse.h"
4562c604 43#include "match.h"
4016731b 44#include "logger.h"
212380e3
AC
45#include "s_user.h"
46#include "cache.h"
47#include "s_newconf.h"
48
49#define CHALLENGE_WIDTH BUFSIZE - (NICKLEN + HOSTLEN + 12)
50#define CHALLENGE_EXPIRES 180 /* 180 seconds should be more than long enough */
51#define CHALLENGE_SECRET_LENGTH 128 /* how long our challenge secret should be */
52
53#ifndef HAVE_LIBCRYPTO
eeabf33a
EM
54
55static const char challenge_desc[] = "Does nothing as OpenSSL was not enabled.";
56
212380e3
AC
57/* Maybe this should be an error or something?-davidt */
58/* now it is -larne */
eeabf33a 59static int challenge_load(void)
212380e3 60{
a9227555 61 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
212380e3
AC
62 "Challenge module not loaded because OpenSSL is not available.");
63 ilog(L_MAIN, "Challenge module not loaded because OpenSSL is not available.");
64 return -1;
212380e3
AC
65}
66
5544da98 67DECLARE_MODULE_AV2(challenge, challenge_load, NULL, NULL, NULL, NULL, NULL, NULL, challenge_desc);
212380e3
AC
68#else
69
eeabf33a
EM
70static const char challenge_desc[] =
71 "Provides the challenge-response facility used for becoming an IRC operator";
72
3c7d6fcc 73static void m_challenge(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
212380e3
AC
74
75/* We have openssl support, so include /CHALLENGE */
76struct Message challenge_msgtab = {
7baa37a9 77 "CHALLENGE", 0, 0, 0, 0,
212380e3
AC
78 {mg_unreg, {m_challenge, 2}, mg_ignore, mg_ignore, mg_ignore, {m_challenge, 2}}
79};
80
81mapi_clist_av1 challenge_clist[] = { &challenge_msgtab, NULL };
5544da98 82
5544da98 83DECLARE_MODULE_AV2(challenge, NULL, NULL, challenge_clist, NULL, NULL, NULL, NULL, challenge_desc);
212380e3 84
8e9a7418 85#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
86static bool generate_challenge(char **r_challenge, unsigned char **r_response, EVP_PKEY *key);
87#else
88static bool generate_challenge(char **r_challenge, unsigned char **r_response, RSA *key);
89#endif
212380e3
AC
90
91static void
92cleanup_challenge(struct Client *target_p)
93{
94 if(target_p->localClient == NULL)
95 return;
55abcbb2 96
637c4932 97 rb_free(target_p->localClient->challenge);
ed3ca2ff 98 rb_free(target_p->user->opername);
212380e3 99 target_p->localClient->challenge = NULL;
ed3ca2ff 100 target_p->user->opername = NULL;
212380e3
AC
101 target_p->localClient->chal_time = 0;
102}
103
104/*
105 * m_challenge - generate RSA challenge for wouldbe oper
212380e3 106 * parv[1] = operator to challenge for, or +response
212380e3 107 */
3c7d6fcc 108static void
428ca87b 109m_challenge(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
212380e3
AC
110{
111 struct oper_conf *oper_p;
112 char *challenge = NULL; /* to placate gcc */
55abcbb2 113 char chal_line[CHALLENGE_WIDTH];
212380e3
AC
114 unsigned char *b_response;
115 size_t cnt;
116 int len = 0;
117
40ecb85a 118 if (ConfigFileEntry.oper_secure_only && !IsSecureClient(source_p))
119 {
120 sendto_one_notice(source_p, ":You must be using a secure connection to /CHALLENGE on this server");
121 if (ConfigFileEntry.failed_oper_notice)
122 {
123 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
124 "Failed CHALLENGE attempt - missing secure connection by %s (%s@%s)",
125 source_p->name, source_p->username, source_p->host);
126 }
127 return;
128 }
129
212380e3
AC
130 /* if theyre an oper, reprint oper motd and ignore */
131 if(IsOper(source_p))
132 {
133 sendto_one(source_p, form_str(RPL_YOUREOPER), me.name, source_p->name);
134 send_oper_motd(source_p);
3c7d6fcc 135 return;
212380e3
AC
136 }
137
138 if(*parv[1] == '+')
139 {
140 /* Ignore it if we aren't expecting this... -A1kmm */
141 if(!source_p->localClient->challenge)
3c7d6fcc 142 return;
212380e3 143
e3354945 144 if((rb_current_time() - source_p->localClient->chal_time) > CHALLENGE_EXPIRES)
212380e3
AC
145 {
146 sendto_one(source_p, form_str(ERR_PASSWDMISMATCH), me.name, source_p->name);
147 ilog(L_FOPER, "EXPIRED CHALLENGE (%s) by (%s!%s@%s) (%s)",
ed3ca2ff 148 source_p->user->opername, source_p->name,
212380e3
AC
149 source_p->username, source_p->host, source_p->sockhost);
150
151 if(ConfigFileEntry.failed_oper_notice)
152 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
153 "Expired CHALLENGE attempt by %s (%s@%s)",
154 source_p->name, source_p->username,
155 source_p->host);
156 cleanup_challenge(source_p);
3c7d6fcc 157 return;
212380e3
AC
158 }
159
6493f05d
EM
160 parv[1]++;
161 b_response = rb_base64_decode((const unsigned char *)parv[1], strlen(parv[1]), &len);
212380e3
AC
162
163 if(len != SHA_DIGEST_LENGTH ||
164 memcmp(source_p->localClient->challenge, b_response, SHA_DIGEST_LENGTH))
165 {
166 sendto_one(source_p, form_str(ERR_PASSWDMISMATCH), me.name, source_p->name);
167 ilog(L_FOPER, "FAILED CHALLENGE (%s) by (%s!%s@%s) (%s)",
ed3ca2ff 168 source_p->user->opername, source_p->name,
212380e3
AC
169 source_p->username, source_p->host, source_p->sockhost);
170
171 if(ConfigFileEntry.failed_oper_notice)
172 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
173 "Failed CHALLENGE attempt by %s (%s@%s)",
174 source_p->name, source_p->username,
175 source_p->host);
176
637c4932 177 rb_free(b_response);
212380e3 178 cleanup_challenge(source_p);
3c7d6fcc 179 return;
212380e3
AC
180 }
181
637c4932 182 rb_free(b_response);
212380e3 183
55abcbb2
KB
184 oper_p = find_oper_conf(source_p->username, source_p->orighost,
185 source_p->sockhost,
ed3ca2ff 186 source_p->user->opername);
212380e3
AC
187
188 if(oper_p == NULL)
189 {
76169ea7 190 sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST));
c7905202 191 ilog(L_FOPER, "FAILED CHALLENGE (%s) by (%s!%s@%s) (%s)",
ed3ca2ff 192 source_p->user->opername, source_p->name,
212380e3
AC
193 source_p->username, source_p->host,
194 source_p->sockhost);
195
196 if(ConfigFileEntry.failed_oper_notice)
197 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
198 "Failed CHALLENGE attempt - host mismatch by %s (%s@%s)",
199 source_p->name, source_p->username,
200 source_p->host);
73520cd1 201 cleanup_challenge(source_p);
3c7d6fcc 202 return;
212380e3
AC
203 }
204
205 cleanup_challenge(source_p);
206
207 oper_up(source_p, oper_p);
208
c7905202 209 ilog(L_OPERED, "CHALLENGE %s by %s!%s@%s (%s)",
ed3ca2ff 210 source_p->user->opername, source_p->name,
212380e3 211 source_p->username, source_p->host, source_p->sockhost);
3c7d6fcc 212 return;
212380e3
AC
213 }
214
215 cleanup_challenge(source_p);
216
55abcbb2 217 oper_p = find_oper_conf(source_p->username, source_p->orighost,
212380e3
AC
218 source_p->sockhost, parv[1]);
219
220 if(oper_p == NULL)
221 {
76169ea7 222 sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST));
c7905202 223 ilog(L_FOPER, "FAILED CHALLENGE (%s) by (%s!%s@%s) (%s)",
212380e3
AC
224 parv[1], source_p->name,
225 source_p->username, source_p->host, source_p->sockhost);
226
227 if(ConfigFileEntry.failed_oper_notice)
228 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
3b24363e
AJ
229 "Failed CHALLENGE attempt - user@host mismatch or no operator block for %s by %s (%s@%s)",
230 parv[1], source_p->name, source_p->username, source_p->host);
3c7d6fcc 231 return;
212380e3
AC
232 }
233
234 if(!oper_p->rsa_pubkey)
235 {
5366977b 236 sendto_one_notice(source_p, ":I'm sorry, PK authentication is not enabled for your oper{} block.");
3c7d6fcc 237 return;
212380e3
AC
238 }
239
35eccf49 240 if(IsOperConfNeedSSL(oper_p) && !IsSecureClient(source_p))
b1594414 241 {
76169ea7 242 sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST));
b1594414
JT
243 ilog(L_FOPER, "FAILED CHALLENGE (%s) by (%s!%s@%s) (%s) -- requires SSL/TLS",
244 parv[1], source_p->name, source_p->username, source_p->host,
245 source_p->sockhost);
246
247 if(ConfigFileEntry.failed_oper_notice)
248 {
a9227555 249 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
b1594414
JT
250 "Failed CHALLENGE attempt - missing SSL/TLS by %s (%s@%s)",
251 source_p->name, source_p->username, source_p->host);
252 }
3c7d6fcc 253 return;
b1594414
JT
254 }
255
ed8b3d69
AC
256 if (oper_p->certfp != NULL)
257 {
f956cb0f 258 if (source_p->certfp == NULL || rb_strcasecmp(source_p->certfp, oper_p->certfp))
ed8b3d69
AC
259 {
260 sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST));
c7905202 261 ilog(L_FOPER, "FAILED CHALLENGE (%s) by (%s!%s@%s) (%s) -- client certificate fingerprint mismatch",
fad065bb 262 parv[1], source_p->name,
ed8b3d69
AC
263 source_p->username, source_p->host, source_p->sockhost);
264
265 if(ConfigFileEntry.failed_oper_notice)
266 {
a9227555 267 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
c7905202 268 "Failed CHALLENGE attempt - client certificate fingerprint mismatch by %s (%s@%s)",
ed8b3d69
AC
269 source_p->name, source_p->username, source_p->host);
270 }
3c7d6fcc 271 return;
ed8b3d69
AC
272 }
273 }
274
3c7d6fcc 275 if(generate_challenge(&challenge, &(source_p->localClient->challenge), oper_p->rsa_pubkey))
212380e3
AC
276 {
277 char *chal = challenge;
e3354945 278 source_p->localClient->chal_time = rb_current_time();
212380e3
AC
279 for(;;)
280 {
f427c8b0 281 cnt = rb_strlcpy(chal_line, chal, CHALLENGE_WIDTH);
212380e3 282 sendto_one(source_p, form_str(RPL_RSACHALLENGE2), me.name, source_p->name, chal_line);
73520cd1 283 if(cnt >= CHALLENGE_WIDTH)
212380e3
AC
284 chal += CHALLENGE_WIDTH - 1;
285 else
286 break;
55abcbb2 287
212380e3 288 }
55abcbb2 289 sendto_one(source_p, form_str(RPL_ENDOFRSACHALLENGE2),
212380e3 290 me.name, source_p->name);
637c4932 291 rb_free(challenge);
ed3ca2ff 292 source_p->user->opername = rb_strdup(oper_p->name);
212380e3
AC
293 }
294 else
295 sendto_one_notice(source_p, ":Failed to generate challenge.");
212380e3
AC
296}
297
3c7d6fcc 298static bool
8e9a7418 299#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
300generate_challenge(char **r_challenge, unsigned char **r_response, EVP_PKEY *key)
301#else
302generate_challenge(char **r_challenge, unsigned char **r_response, RSA *key)
303#endif
212380e3 304{
8e9a7418 305 unsigned char secret[CHALLENGE_SECRET_LENGTH];
306 unsigned char *tmp = NULL;
212380e3
AC
307 unsigned long e = 0;
308 unsigned long cnt = 0;
8e9a7418 309 bool retval = false;
310 size_t length;
311 EVP_MD_CTX *mctx = NULL;
312#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
313 EVP_PKEY_CTX *pctx = NULL;
314#endif
212380e3 315
8e9a7418 316 if(!rb_get_random(secret, sizeof secret))
317 return false;
318
319 if((*r_response = rb_malloc(SHA_DIGEST_LENGTH)) == NULL)
3c7d6fcc 320 return false;
212380e3 321
8e9a7418 322 if((mctx = EVP_MD_CTX_new()) == NULL)
323 goto fail;
212380e3 324
8e9a7418 325 if(EVP_DigestInit(mctx, EVP_sha1()) < 1)
326 goto fail;
f55930ac 327
8e9a7418 328 if(EVP_DigestUpdate(mctx, secret, sizeof secret) < 1)
329 goto fail;
330
331 if(EVP_DigestFinal(mctx, *r_response, NULL) < 1)
332 goto fail;
333
334#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
335 if((length = (size_t) EVP_PKEY_get_size(key)) < 1)
336 goto fail;
337
338 if((tmp = rb_malloc(length)) == NULL)
339 goto fail;
212380e3 340
8e9a7418 341 if((pctx = EVP_PKEY_CTX_new(key, NULL)) == NULL)
342 goto fail;
343
344 if(EVP_PKEY_encrypt_init(pctx) < 1)
345 goto fail;
346
347 if(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_OAEP_PADDING) < 1)
348 goto fail;
349
350 if(EVP_PKEY_encrypt(pctx, tmp, &length, secret, sizeof secret) < 1)
351 goto fail;
352#else
353 if((length = (size_t) RSA_size(key)) < 1)
354 goto fail;
355
356 if((tmp = rb_malloc(length)) == NULL)
357 goto fail;
358
359 if(RSA_public_encrypt(sizeof secret, secret, tmp, key, RSA_PKCS1_OAEP_PADDING) < 1)
360 goto fail;
361#endif
362
363 if((*r_challenge = (char *) rb_base64_encode(tmp, (int) length)) == NULL)
364 goto fail;
365
366 retval = true;
367 goto done;
368
369fail:
212380e3
AC
370 while ((cnt < 100) && (e = ERR_get_error()))
371 {
8e9a7418 372 ilog(L_MAIN, "OpenSSL Error (CHALLENGE): %s", ERR_error_string(e, 0));
212380e3
AC
373 cnt++;
374 }
375
8e9a7418 376 rb_free(*r_response);
377 *r_response = NULL;
378
379done:
380 EVP_MD_CTX_free(mctx);
381#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
382 EVP_PKEY_CTX_free(pctx);
383#endif
384 rb_free(tmp);
385
386 return retval;
212380e3
AC
387}
388
389#endif /* HAVE_LIBCRYPTO */