2 * syn: a utility bot to manage IRC network access
3 * Copyright (C) 2009-2016 Stephen Bennett
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.
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.
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/>.
23 #define PRF_KEY_LEN 16
24 #define PRF_KEY_HEX_LEN (PRF_KEY_LEN * 2)
25 #define PRF_OUT_LEN 16
27 char *prf_key_hex
= NULL
;
28 uint8_t prf_key
[PRF_KEY_LEN
];
29 bool prf_ready
= false;
31 const char *_decode_hex_ip(const char *hex
)
42 ip
= strtoul(hex
, &endptr
, 16);
46 sprintf(buf
, "%hhu.%hhu.%hhu.%hhu", (ip
>> 24) & 0xff, (ip
>> 16) & 0xff, (ip
>> 8) & 0xff, ip
& 0xff);
50 const char *_get_random_host_part(user_t
*u
)
52 // UID, user, host, '!', '@', NUL
53 static char user_buf
[9 + USERLEN
+ HOSTLEN
+ 3];
54 static char buf
[PRF_OUT_LEN
+ 3];
57 snprintf(user_buf
, sizeof user_buf
, "%s!%s@%s", u
->uid
, u
->user
, u
->host
);
61 syn_debug(2, "PRF key not configured, falling back to random cloaking");
62 for (size_t i
= 0; i
< PRF_OUT_LEN
; ++i
)
64 buf
[i
+ 2] = 'a' + rand() % 26;
69 int siphash(const uint8_t *in
, const size_t inlen
, const uint8_t *k
,
70 uint8_t *out
, const size_t outlen
);
72 uint8_t out
[PRF_OUT_LEN
];
73 siphash((unsigned char*)user_buf
, strlen(user_buf
), prf_key
, out
, PRF_OUT_LEN
);
75 for (size_t i
=0; i
< PRF_OUT_LEN
; ++i
)
77 buf
[i
+ 2] = 'a' + out
[i
] % 26;
81 buf
[PRF_OUT_LEN
+ 2] = 0;
85 // Taken from ircd-seven extensions/sasl_usercloak.c, modified for const correctness
86 static unsigned int fnv_hash_string(const char *str
)
88 unsigned int hash
= 0x811c9dc5; // Magic value for 32-bit fnv1 hash initialisation.
89 unsigned const char *p
= (unsigned const char *)str
;
92 hash
+= (hash
<<1) + (hash
<<4) + (hash
<<7) + (hash
<<8) + (hash
<<24);
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
103 const char *_encode_ident_for_host(const char *str
)
105 // ident + /x- + SUFFIX_HASH_LENGTH, and nul terminator
106 static char buf
[USERLEN
+ SUFFIX_HASH_LENGTH
+ 3 + 1];
107 bool needhash
= false;
110 for (const char *src
= str
; *src
; src
++)
112 if (str
- src
> USERLEN
)
114 slog(LG_ERROR
, "encode_ident_for_host(): tried to encode %s which is too long", str
);
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
== '-')
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
);
141 time_t _syn_parse_duration(const char *s
)
143 time_t duration
= atol(s
);
164 const char *_syn_format_expiry(time_t t
)
166 static char expirybuf
[BUFSIZE
];
169 strftime(expirybuf
, BUFSIZE
, "%d/%m/%Y %H:%M:%S", gmtime(&t
));
173 strcpy(expirybuf
, "never");
179 static void syn_util_config_ready(void *unused
)
181 if (prf_key_hex
== NULL
)
183 slog(LG_ERROR
, "syn/util: could not find 'prf_key' configuration entry");
188 if (strlen(prf_key_hex
) != PRF_KEY_HEX_LEN
)
190 slog(LG_ERROR
, "syn/util: prf_key must be exactly %d hex digits", PRF_KEY_HEX_LEN
);
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
++)
198 if (sscanf(prf_key_hex
+ (i
* 2), "%2" SCNx8
, &prf_key
[i
]) != 1)
200 slog(LG_ERROR
, "syn/util: failed to parse prf_key - must be string of hex digits");
209 static void mod_init(module_t
*m
)
211 use_syn_main_symbols(m
);
213 add_dupstr_conf_item("PRF_KEY", &syn
->conf_table
, 0, &prf_key_hex
, NULL
);
214 hook_add_config_ready(syn_util_config_ready
);
217 static void mod_deinit(module_unload_intent_t unused
)
219 del_conf_item("PRF_KEY", &syn
->conf_table
);
220 hook_del_config_ready(syn_util_config_ready
);
225 "syn/util", false, mod_init
, mod_deinit
,
227 "Stephen Bennett <stephen -at- freenode.net>"