This repository has been archived by the owner on Jun 6, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
util.c
228 lines (194 loc) · 5.96 KB
/
util.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/*
* syn: a utility bot to manage IRC network access
* Copyright (C) 2009-2016 Stephen Bennett
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "atheme.h"
#include "syn.h"
#define PRF_KEY_LEN 16
#define PRF_KEY_HEX_LEN (PRF_KEY_LEN * 2)
#define PRF_OUT_LEN 16
char *prf_key_hex = NULL;
uint8_t prf_key[PRF_KEY_LEN];
bool prf_ready = false;
const char *_decode_hex_ip(const char *hex)
{
static char buf[16];
unsigned int ip = 0;
buf[0] = '\0';
if (strlen(hex) != 8)
return NULL;
char *endptr;
ip = strtoul(hex, &endptr, 16);
if (*endptr)
return NULL;
sprintf(buf, "%hhu.%hhu.%hhu.%hhu", (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
return buf;
}
const char *_get_random_host_part(user_t *u)
{
// UID, user, host, '!', '@', NUL
static char user_buf[9 + USERLEN + HOSTLEN + 3];
static char buf[PRF_OUT_LEN + 3];
strcpy(buf, "x-");
snprintf(user_buf, sizeof user_buf, "%s!%s@%s", u->uid, u->user, u->host);
if (!prf_ready)
{
syn_debug(2, "PRF key not configured, falling back to random cloaking");
for (size_t i = 0; i < PRF_OUT_LEN; ++i)
{
buf[i + 2] = 'a' + rand() % 26;
}
}
else
{
int siphash(const uint8_t *in, const size_t inlen, const uint8_t *k,
uint8_t *out, const size_t outlen);
uint8_t out[PRF_OUT_LEN];
siphash((unsigned char*)user_buf, strlen(user_buf), prf_key, out, PRF_OUT_LEN);
for (size_t i=0; i < PRF_OUT_LEN; ++i)
{
buf[i + 2] = 'a' + out[i] % 26;
}
}
buf[PRF_OUT_LEN + 2] = 0;
return buf;
}
// Taken from ircd-seven extensions/sasl_usercloak.c, modified for const correctness
static unsigned int fnv_hash_string(const char *str)
{
unsigned int hash = 0x811c9dc5; // Magic value for 32-bit fnv1 hash initialisation.
unsigned const char *p = (unsigned const char *)str;
while (*p)
{
hash += (hash<<1) + (hash<<4) + (hash<<7) + (hash<<8) + (hash<<24);
hash ^= *p++;
}
return hash;
}
// Make sure to keep these in agreement.
#define SUFFIX_HASH_LENGTH 8
#define SUFFIX_HASH_FMT "%08ud"
#define SUFFIX_HASH_MODULUS 100000000
const char *_encode_ident_for_host(const char *str)
{
// ident + /x- + SUFFIX_HASH_LENGTH, and nul terminator
static char buf[USERLEN + SUFFIX_HASH_LENGTH + 3 + 1];
bool needhash = false;
char *dst = buf;
for (const char *src = str; *src; src++)
{
if (str - src > USERLEN)
{
slog(LG_ERROR, "encode_ident_for_host(): tried to encode %s which is too long", str);
return NULL;
}
// For now, consider alphanumerics valid, as well as -
// . is technically possible in ident, but might be confused for cloak formatting
// Digits are not allowed unless there was another character successfully reproduced
// since this could otherwise produce output that looks like a CIDR mask,
// which messes with bans and is generally not done.
if (IsAlpha(*src) || (IsDigit(*src) && dst != buf) || *src == '-')
*dst++ = *src;
else
needhash = true;
}
*dst = '\0';
if (needhash)
{
unsigned int hashval = fnv_hash_string(str);
hashval %= SUFFIX_HASH_MODULUS;
snprintf(dst, 3 + SUFFIX_HASH_LENGTH + 1, "/x-" SUFFIX_HASH_FMT, hashval);
}
return buf;
}
time_t _syn_parse_duration(const char *s)
{
time_t duration = atol(s);
while (isdigit(*s))
s++;
switch (*s)
{
case 'H':
case 'h':
duration *= 60;
break;
case 'D':
case 'd':
duration *= 1440;
break;
case 'W':
case 'w':
duration *= 10080;
break;
}
return duration;
}
const char *_syn_format_expiry(time_t t)
{
static char expirybuf[BUFSIZE];
if (t > 0)
{
strftime(expirybuf, BUFSIZE, "%d/%m/%Y %H:%M:%S", gmtime(&t));
}
else
{
strcpy(expirybuf, "never");
}
return expirybuf;
}
static void syn_util_config_ready(void *unused)
{
if (prf_key_hex == NULL)
{
slog(LG_ERROR, "syn/util: could not find 'prf_key' configuration entry");
prf_ready = false;
return;
}
if (strlen(prf_key_hex) != PRF_KEY_HEX_LEN)
{
slog(LG_ERROR, "syn/util: prf_key must be exactly %d hex digits", PRF_KEY_HEX_LEN);
prf_ready = false;
return;
}
// This could be done in a single big sscanf, but let's not do that
for (size_t i = 0; i < PRF_KEY_LEN; i++)
{
if (sscanf(prf_key_hex + (i * 2), "%2" SCNx8, &prf_key[i]) != 1)
{
slog(LG_ERROR, "syn/util: failed to parse prf_key - must be string of hex digits");
prf_ready = false;
return;
}
}
prf_ready = true;
}
static void mod_init(module_t *m)
{
use_syn_main_symbols(m);
add_dupstr_conf_item("PRF_KEY", &syn->conf_table, 0, &prf_key_hex, NULL);
hook_add_config_ready(syn_util_config_ready);
}
static void mod_deinit(module_unload_intent_t unused)
{
del_conf_item("PRF_KEY", &syn->conf_table);
hook_del_config_ready(syn_util_config_ready);
}
DECLARE_MODULE_V1
(
"syn/util", false, mod_init, mod_deinit,
"$Revision$",
"Stephen Bennett <stephen -at- freenode.net>"
);