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