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