]> jfr.im git - irc/freenode/solanum.git/blame - modules/m_challenge.c
make more snotes L_NETWIDE
[irc/freenode/solanum.git] / modules / m_challenge.c
CommitLineData
212380e3
WP
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
WP
23 */
24
25#include "stdinc.h"
26
27#ifdef HAVE_LIBCRYPTO
28#include <openssl/pem.h>
29#include <openssl/rand.h>
30#include <openssl/rsa.h>
31#include <openssl/md5.h>
32#include <openssl/bn.h>
33#include <openssl/evp.h>
34#include <openssl/err.h>
35#endif
36
212380e3
WP
37#include "client.h"
38#include "ircd.h"
39#include "modules.h"
40#include "numeric.h"
41#include "send.h"
42#include "s_conf.h"
43#include "msg.h"
44#include "parse.h"
4562c604 45#include "match.h"
4016731b 46#include "logger.h"
212380e3
WP
47#include "s_user.h"
48#include "cache.h"
49#include "s_newconf.h"
50
51#define CHALLENGE_WIDTH BUFSIZE - (NICKLEN + HOSTLEN + 12)
52#define CHALLENGE_EXPIRES 180 /* 180 seconds should be more than long enough */
53#define CHALLENGE_SECRET_LENGTH 128 /* how long our challenge secret should be */
54
55#ifndef HAVE_LIBCRYPTO
eeabf33a
EM
56
57static const char challenge_desc[] = "Does nothing as OpenSSL was not enabled.";
58
212380e3
WP
59/* Maybe this should be an error or something?-davidt */
60/* now it is -larne */
eeabf33a 61static int challenge_load(void)
212380e3 62{
a9227555 63 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
212380e3
WP
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;
212380e3
WP
67}
68
5544da98 69DECLARE_MODULE_AV2(challenge, challenge_load, NULL, NULL, NULL, NULL, NULL, NULL, challenge_desc);
212380e3
WP
70#else
71
eeabf33a
EM
72static const char challenge_desc[] =
73 "Provides the challenge-response facility used for becoming an IRC operator";
74
3c7d6fcc 75static void m_challenge(struct MsgBuf *, struct Client *, struct Client *, int, const char **);
212380e3
WP
76
77/* We have openssl support, so include /CHALLENGE */
78struct Message challenge_msgtab = {
7baa37a9 79 "CHALLENGE", 0, 0, 0, 0,
212380e3
WP
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 };
5544da98 84
5544da98 85DECLARE_MODULE_AV2(challenge, NULL, NULL, challenge_clist, NULL, NULL, NULL, NULL, challenge_desc);
212380e3 86
3c7d6fcc 87static bool generate_challenge(char **r_challenge, char **r_response, RSA * key);
212380e3
WP
88
89static void
90cleanup_challenge(struct Client *target_p)
91{
92 if(target_p->localClient == NULL)
93 return;
55abcbb2 94
637c4932 95 rb_free(target_p->localClient->challenge);
ed3ca2ff 96 rb_free(target_p->user->opername);
212380e3 97 target_p->localClient->challenge = NULL;
ed3ca2ff 98 target_p->user->opername = NULL;
212380e3
WP
99 target_p->localClient->chal_time = 0;
100}
101
102/*
103 * m_challenge - generate RSA challenge for wouldbe oper
212380e3 104 * parv[1] = operator to challenge for, or +response
212380e3 105 */
3c7d6fcc 106static void
428ca87b 107m_challenge(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
212380e3
WP
108{
109 struct oper_conf *oper_p;
110 char *challenge = NULL; /* to placate gcc */
55abcbb2 111 char chal_line[CHALLENGE_WIDTH];
212380e3
WP
112 unsigned char *b_response;
113 size_t cnt;
114 int len = 0;
115
116 /* if theyre an oper, reprint oper motd and ignore */
117 if(IsOper(source_p))
118 {
119 sendto_one(source_p, form_str(RPL_YOUREOPER), me.name, source_p->name);
120 send_oper_motd(source_p);
3c7d6fcc 121 return;
212380e3
WP
122 }
123
124 if(*parv[1] == '+')
125 {
126 /* Ignore it if we aren't expecting this... -A1kmm */
127 if(!source_p->localClient->challenge)
3c7d6fcc 128 return;
212380e3 129
e3354945 130 if((rb_current_time() - source_p->localClient->chal_time) > CHALLENGE_EXPIRES)
212380e3
WP
131 {
132 sendto_one(source_p, form_str(ERR_PASSWDMISMATCH), me.name, source_p->name);
133 ilog(L_FOPER, "EXPIRED CHALLENGE (%s) by (%s!%s@%s) (%s)",
ed3ca2ff 134 source_p->user->opername, source_p->name,
212380e3
WP
135 source_p->username, source_p->host, source_p->sockhost);
136
137 if(ConfigFileEntry.failed_oper_notice)
138 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
139 "Expired CHALLENGE attempt by %s (%s@%s)",
140 source_p->name, source_p->username,
141 source_p->host);
142 cleanup_challenge(source_p);
3c7d6fcc 143 return;
212380e3
WP
144 }
145
6493f05d
EM
146 parv[1]++;
147 b_response = rb_base64_decode((const unsigned char *)parv[1], strlen(parv[1]), &len);
212380e3
WP
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)",
ed3ca2ff 154 source_p->user->opername, source_p->name,
212380e3
WP
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
637c4932 163 rb_free(b_response);
212380e3 164 cleanup_challenge(source_p);
3c7d6fcc 165 return;
212380e3
WP
166 }
167
637c4932 168 rb_free(b_response);
212380e3 169
55abcbb2
KB
170 oper_p = find_oper_conf(source_p->username, source_p->orighost,
171 source_p->sockhost,
ed3ca2ff 172 source_p->user->opername);
212380e3
WP
173
174 if(oper_p == NULL)
175 {
76169ea7 176 sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST));
212380e3 177 ilog(L_FOPER, "FAILED OPER (%s) by (%s!%s@%s) (%s)",
ed3ca2ff 178 source_p->user->opername, source_p->name,
212380e3
WP
179 source_p->username, source_p->host,
180 source_p->sockhost);
181
182 if(ConfigFileEntry.failed_oper_notice)
183 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
184 "Failed CHALLENGE attempt - host mismatch by %s (%s@%s)",
185 source_p->name, source_p->username,
186 source_p->host);
3c7d6fcc 187 return;
212380e3
WP
188 }
189
190 cleanup_challenge(source_p);
191
192 oper_up(source_p, oper_p);
193
194 ilog(L_OPERED, "OPER %s by %s!%s@%s (%s)",
ed3ca2ff 195 source_p->user->opername, source_p->name,
212380e3 196 source_p->username, source_p->host, source_p->sockhost);
3c7d6fcc 197 return;
212380e3
WP
198 }
199
200 cleanup_challenge(source_p);
201
55abcbb2 202 oper_p = find_oper_conf(source_p->username, source_p->orighost,
212380e3
WP
203 source_p->sockhost, parv[1]);
204
205 if(oper_p == NULL)
206 {
76169ea7 207 sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST));
212380e3
WP
208 ilog(L_FOPER, "FAILED OPER (%s) by (%s!%s@%s) (%s)",
209 parv[1], source_p->name,
210 source_p->username, source_p->host, source_p->sockhost);
211
212 if(ConfigFileEntry.failed_oper_notice)
213 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
214 "Failed CHALLENGE attempt - host mismatch by %s (%s@%s)",
215 source_p->name, source_p->username, source_p->host);
3c7d6fcc 216 return;
212380e3
WP
217 }
218
219 if(!oper_p->rsa_pubkey)
220 {
5366977b 221 sendto_one_notice(source_p, ":I'm sorry, PK authentication is not enabled for your oper{} block.");
3c7d6fcc 222 return;
212380e3
WP
223 }
224
35eccf49 225 if(IsOperConfNeedSSL(oper_p) && !IsSecureClient(source_p))
b1594414 226 {
76169ea7 227 sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST));
b1594414
JT
228 ilog(L_FOPER, "FAILED CHALLENGE (%s) by (%s!%s@%s) (%s) -- requires SSL/TLS",
229 parv[1], source_p->name, source_p->username, source_p->host,
230 source_p->sockhost);
231
232 if(ConfigFileEntry.failed_oper_notice)
233 {
a9227555 234 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
b1594414
JT
235 "Failed CHALLENGE attempt - missing SSL/TLS by %s (%s@%s)",
236 source_p->name, source_p->username, source_p->host);
237 }
3c7d6fcc 238 return;
b1594414
JT
239 }
240
ed8b3d69
WP
241 if (oper_p->certfp != NULL)
242 {
f956cb0f 243 if (source_p->certfp == NULL || rb_strcasecmp(source_p->certfp, oper_p->certfp))
ed8b3d69
WP
244 {
245 sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST));
246 ilog(L_FOPER, "FAILED OPER (%s) by (%s!%s@%s) (%s) -- client certificate fingerprint mismatch",
fad065bb 247 parv[1], source_p->name,
ed8b3d69
WP
248 source_p->username, source_p->host, source_p->sockhost);
249
250 if(ConfigFileEntry.failed_oper_notice)
251 {
a9227555 252 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
ed8b3d69
WP
253 "Failed OPER attempt - client certificate fingerprint mismatch by %s (%s@%s)",
254 source_p->name, source_p->username, source_p->host);
255 }
3c7d6fcc 256 return;
ed8b3d69
WP
257 }
258 }
259
3c7d6fcc 260 if(generate_challenge(&challenge, &(source_p->localClient->challenge), oper_p->rsa_pubkey))
212380e3
WP
261 {
262 char *chal = challenge;
e3354945 263 source_p->localClient->chal_time = rb_current_time();
212380e3
WP
264 for(;;)
265 {
f427c8b0 266 cnt = rb_strlcpy(chal_line, chal, CHALLENGE_WIDTH);
212380e3
WP
267 sendto_one(source_p, form_str(RPL_RSACHALLENGE2), me.name, source_p->name, chal_line);
268 if(cnt > CHALLENGE_WIDTH)
269 chal += CHALLENGE_WIDTH - 1;
270 else
271 break;
55abcbb2 272
212380e3 273 }
55abcbb2 274 sendto_one(source_p, form_str(RPL_ENDOFRSACHALLENGE2),
212380e3 275 me.name, source_p->name);
637c4932 276 rb_free(challenge);
ed3ca2ff 277 source_p->user->opername = rb_strdup(oper_p->name);
212380e3
WP
278 }
279 else
280 sendto_one_notice(source_p, ":Failed to generate challenge.");
212380e3
WP
281}
282
3c7d6fcc 283static bool
212380e3
WP
284generate_challenge(char **r_challenge, char **r_response, RSA * rsa)
285{
286 SHA_CTX ctx;
287 unsigned char secret[CHALLENGE_SECRET_LENGTH], *tmp;
288 unsigned long length;
289 unsigned long e = 0;
290 unsigned long cnt = 0;
291 int ret;
292
293 if(!rsa)
3c7d6fcc 294 return false;
f55930ac 295 if(rb_get_random(secret, CHALLENGE_SECRET_LENGTH))
212380e3
WP
296 {
297 SHA1_Init(&ctx);
f55930ac
VY
298 SHA1_Update(&ctx, (uint8_t *)secret, CHALLENGE_SECRET_LENGTH);
299 *r_response = malloc(SHA_DIGEST_LENGTH);
300 SHA1_Final((uint8_t *)*r_response, &ctx);
212380e3
WP
301
302 length = RSA_size(rsa);
eddc2ab6 303 tmp = rb_malloc(length);
212380e3
WP
304 ret = RSA_public_encrypt(CHALLENGE_SECRET_LENGTH, secret, tmp, rsa, RSA_PKCS1_OAEP_PADDING);
305
f55930ac 306 if(ret >= 0)
212380e3 307 {
f55930ac 308 *r_challenge = (char *)rb_base64_encode(tmp, ret);
637c4932 309 rb_free(tmp);
3c7d6fcc 310 return true;
212380e3 311 }
f55930ac 312
637c4932
VY
313 rb_free(tmp);
314 rb_free(*r_response);
212380e3
WP
315 *r_response = NULL;
316 }
317
318 ERR_load_crypto_strings();
319 while ((cnt < 100) && (e = ERR_get_error()))
320 {
321 ilog(L_MAIN, "SSL error: %s", ERR_error_string(e, 0));
322 cnt++;
323 }
324
3c7d6fcc 325 return false;
212380e3
WP
326}
327
328#endif /* HAVE_LIBCRYPTO */