1 /*
2 * aprsc
3 *
4 * (c) Heikki Hannikainen, OH7LZB <hessu@hes.iki.fi>
5 *
6 * This program is licensed under the BSD license, which can be found
7 * in the file LICENSE.
8 *
9 */
10
11 #define _GNU_SOURCE
12
13 #include <string.h>
14 #include <strings.h>
15 #include <ctype.h>
16
17 #include "login.h"
18 #include "hmalloc.h"
19 #include "hlog.h"
20 #include "passcode.h"
21 #include "incoming.h"
22 #include "config.h"
23 #include "filter.h"
24 #include "clientlist.h"
25 #include "parse_qc.h"
26 #include "ssl.h"
27
28 /* a static list of usernames which are not allowed to log in */
29 static const char *disallow_login_usernames[] = {
30 "pass", /* a sign of "user pass -1" login with no configured username */
31 NULL
32 };
33
34 /* a static list of unmaintained applications which receive some
35 * special "treatment"
36 */
37 static const char *quirks_mode_blacklist[] = {
38 "HR-IXPWIND", /* Haute Networks HauteWIND: transmits LF NUL for line termination */
39 "HR-IXP-WIND", /* Variation of Haute Networks HauteWIND */
40 "IXP-WIND", /* Variation of Haute Networks HauteWIND */
41 "Oww/", /* One-Wire Weather CWOP client sends a NUL byte in beginning of packet (in end of previous line) */
42 NULL
43 };
44
45 /*
46 * Parse the login string in a HTTP POST or UDP submit packet
47 * Argh, why are these not in standard POST parameters in HTTP?
48 *
49 * TODO: Used for UDP too, so should not say HTTP in log errors...
50 */
51
http_udp_upload_login(const char * addr_rem,char * s,char ** username,const char * log_source)52 int http_udp_upload_login(const char *addr_rem, char *s, char **username, const char *log_source)
53 {
54 int argc;
55 char *argv[256];
56 int i;
57 int username_len;
58
59 /* parse to arguments */
60 if ((argc = parse_args_noshell(argv, s)) == 0)
61 return -1;
62
63 if (argc < 2) {
64 hlog(LOG_WARNING, "%s: %s: Invalid login string, too few arguments: '%s'", addr_rem, log_source, s);
65 return -1;
66 }
67
68 if (strcasecmp(argv[0], "user") != 0) {
69 hlog(LOG_WARNING, "%s: %s: Invalid login string, no 'user': '%s'", addr_rem, log_source, s);
70 return -1;
71 }
72
73 *username = argv[1];
74 username_len = strlen(*username);
75 /* limit username length */
76 if (username_len > CALLSIGNLEN_MAX) {
77 hlog(LOG_WARNING, "%s: %s: Invalid login string, too long 'user' username: '%s'", addr_rem, log_source, *username);
78 return -1;
79 }
80
81 /* check the username against a static list of disallowed usernames */
82 for (i = 0; (disallow_login_usernames[i]); i++) {
83 if (strcasecmp(*username, disallow_login_usernames[i]) == 0) {
84 hlog(LOG_WARNING, "%s: %s: Login by user '%s' not allowed", addr_rem, log_source, *username);
85 return -1;
86 }
87 }
88
89 /* check the username against a dynamic list of disallowed usernames */
90 if (disallow_login_glob && check_call_glob_match(disallow_login_glob, *username, username_len)) {
91 hlog(LOG_WARNING, "%s: %s: Login by user '%s' not allowed due to config", addr_rem, log_source, *username);
92 return -1;
93 }
94
95 /* make sure the callsign is OK on the APRS-IS */
96 if (check_invalid_q_callsign(*username, username_len)) {
97 hlog(LOG_WARNING, "%s: %s: Invalid login string, invalid 'user': '%s'", addr_rem, log_source, *username);
98 return -1;
99 }
100
101 int given_passcode = -1;
102 int validated = 0;
103
104 for (i = 2; i < argc; i++) {
105 if (strcasecmp(argv[i], "pass") == 0) {
106 if (++i >= argc) {
107 hlog(LOG_WARNING, "%s (%s): %s: No passcode after pass command", addr_rem, log_source, username);
108 break;
109 }
110
111 given_passcode = atoi(argv[i]);
112 if (given_passcode >= 0)
113 if (given_passcode == aprs_passcode(*username))
114 validated = 1;
115 } else if (strcasecmp(argv[i], "vers") == 0) {
116 if (i+2 >= argc) {
117 hlog(LOG_DEBUG, "%s (%s): %s: No application name and version after vers command", addr_rem, username, log_source);
118 break;
119 }
120
121 // skip app name and version
122 i += 2;
123 }
124 }
125
126 return validated;
127 }
128
129 /*
130 * Check if string haystack starts with needle, return 1 if true
131 */
132
prefixmatch(const char * haystack,const char * needle)133 static int prefixmatch(const char *haystack, const char *needle)
134 {
135 do {
136 if (*needle == 0)
137 return 1; /* we're at the end of the needle, and no mismatches found */
138
139 if (*haystack == 0)
140 return 0; /* haystack is shorter than needle, cannot match */
141
142 if (*haystack != *needle)
143 return 0; /* mismatch found... */
144
145 /* advance pointers */
146 haystack++;
147 needle++;
148 } while (1);
149 }
150
151 /*
152 * Set and sanitize application name and version strings
153 */
154
login_set_app_name(struct client_t * c,const char * app_name,const char * app_ver)155 void login_set_app_name(struct client_t *c, const char *app_name, const char *app_ver)
156 {
157 int i;
158
159 strncpy(c->app_name, app_name, sizeof(c->app_name));
160 c->app_name[sizeof(c->app_name)-1] = 0;
161 sanitize_ascii_string(c->app_name);
162
163 strncpy(c->app_version, app_ver, sizeof(c->app_version));
164 c->app_version[sizeof(c->app_version)-1] = 0;
165 sanitize_ascii_string(c->app_version);
166
167 /* check the application name against a static list of broken apps */
168 if (quirks_mode) {
169 c->quirks_mode = 1;
170 return;
171 }
172
173 c->quirks_mode = 0;
174 for (i = 0; (quirks_mode_blacklist[i]); i++) {
175 if (prefixmatch(c->app_name, quirks_mode_blacklist[i])) {
176 hlog(LOG_DEBUG, "%s/%s: Enabling quirks mode for application %s %s",
177 c->addr_rem, c->username, c->app_name, c->app_version);
178 c->quirks_mode = 1;
179 break;
180 }
181 }
182
183 }
184
login_setup_udp_feed(struct client_t * c,int port)185 int login_setup_udp_feed(struct client_t *c, int port)
186 {
187 if (!c->udpclient)
188 return -1;
189
190 c->udp_port = port;
191 c->udpaddr = c->addr;
192 if (c->udpaddr.sa.sa_family == AF_INET) {
193 c->udpaddr.si.sin_port = htons(c->udp_port);
194 c->udpaddrlen = sizeof(c->udpaddr.si);
195 } else {
196 c->udpaddr.si6.sin6_port = htons(c->udp_port);
197 c->udpaddrlen = sizeof(c->udpaddr.si6);
198 }
199
200 inbound_connects_account(3, c->udpclient->portaccount); /* "3" = udp, not listening.. */
201
202 return 0;
203 }
204
205 /*
206 * login.c: works in the context of the worker thread
207 */
208
login_handler(struct worker_t * self,struct client_t * c,int l4proto,char * s,int len)209 int login_handler(struct worker_t *self, struct client_t *c, int l4proto, char *s, int len)
210 {
211 int argc;
212 char *argv[256];
213 int i, rc;
214
215 /* make it null-terminated for our string processing */
216 /* TODO: do not modify incoming stream - make s a const char! */
217 char *e = s + len;
218 *e = 0;
219 hlog_packet(LOG_DEBUG, s, len, "%s: login string: ", c->addr_rem);
220
221 /* parse to arguments */
222 if ((argc = parse_args_noshell(argv, s)) == 0 || *argv[0] == '#')
223 return 0;
224
225 if (argc < 2) {
226 hlog(LOG_WARNING, "%s: Invalid login string, too few arguments: '%s'", c->addr_rem, s);
227 rc = client_printf(self, c, "# Invalid login string, too few arguments\r\n");
228 goto failed_login;
229 }
230
231 if (strcasecmp(argv[0], "user") != 0) {
232 if (strcasecmp(argv[0], "GET") == 0)
233 c->failed_cmds = 10; /* bail out right away for a HTTP client */
234
235 c->failed_cmds++;
236 hlog(LOG_WARNING, "%s: Invalid login string, no 'user': '%s'", c->addr_rem, s);
237 rc = client_printf(self, c, "# Invalid login command\r\n");
238 goto failed_login;
239 }
240
241 char *username = argv[1];
242
243 /* limit username length */
244 if (strlen(username) > CALLSIGNLEN_MAX) {
245 hlog(LOG_WARNING, "%s: Invalid login string, too long 'user' username: '%s'", c->addr_rem, username);
246 username[CALLSIGNLEN_MAX] = 0;
247 rc = client_printf(self, c, "# Invalid username format\r\n");
248 goto failed_login;
249 }
250
251 /* ok, it's somewhat valid, write it down */
252 strncpy(c->username, username, sizeof(c->username));
253 c->username[sizeof(c->username)-1] = 0;
254 c->username_len = strlen(c->username);
255
256 /* check the username against a static list of disallowed usernames */
257 for (i = 0; (disallow_login_usernames[i]); i++) {
258 if (strcasecmp(c->username, disallow_login_usernames[i]) == 0) {
259 hlog(LOG_WARNING, "%s: Login by user '%s' not allowed", c->addr_rem, c->username);
260 rc = client_printf(self, c, "# Login by user not allowed\r\n");
261 goto failed_login;
262 }
263 }
264
265 /* check the username against a dynamic list of disallowed usernames */
266 if (disallow_login_glob && check_call_glob_match(disallow_login_glob, c->username, c->username_len)) {
267 hlog(LOG_WARNING, "%s: Login by user '%s' not allowed due to config", c->addr_rem, c->username);
268 rc = client_printf(self, c, "# Login by user not allowed\r\n");
269 goto failed_login;
270 }
271
272 /* make sure the callsign is OK on the APRS-IS */
273 if (check_invalid_q_callsign(c->username, c->username_len)) {
274 hlog(LOG_WARNING, "%s: Invalid login string, invalid 'user': '%s'", c->addr_rem, c->username);
275 rc = client_printf(self, c, "# Invalid username format, not allowed\r\n");
276 goto failed_login;
277 }
278
279 /* make sure the client's callsign is not my Server ID */
280 if (strcasecmp(c->username, serverid) == 0) {
281 hlog(LOG_WARNING, "%s: Invalid login string, username equals our serverid: '%s'", c->addr_rem, c->username);
282 rc = client_printf(self, c, "# Login by user not allowed (our serverid)\r\n");
283 goto failed_login;
284 }
285
286 /* if SSL client cert verification is enabled, check it */
287 int ssl_validated = 0;
288 #ifdef USE_SSL
289 if (c->ssl_con && c->ssl_con->validate) {
290 hlog(LOG_DEBUG, "%s/%s: login: doing SSL client cert validation", c->addr_rem, c->username);
291 int ssl_res = ssl_validate_peer_cert_phase1(c);
292 if (ssl_res == 0)
293 ssl_res = ssl_validate_peer_cert_phase2(c);
294
295 if (ssl_res == 0) {
296 c->validated = VALIDATED_STRONG;
297 ssl_validated = 1;
298 } else {
299 hlog(LOG_WARNING, "%s/%s: SSL client cert validation failed: %s", c->addr_rem, c->username, ssl_strerror(ssl_res));
300 if (ssl_res == SSL_VALIDATE_CLIENT_CERT_UNVERIFIED)
301 rc = client_printf(self, c, "# Client certificate not accepted: %s\r\n", X509_verify_cert_error_string(c->ssl_con->ssl_err_code));
302 else
303 rc = client_printf(self, c, "# Client certificate authentication failed: %s\r\n", ssl_strerror(ssl_res));
304 c->failed_cmds = 10; /* bail out right away for a HTTP client */
305 goto failed_login;
306 }
307 }
308 #endif
309
310
311 int given_passcode = -1;
312
313 for (i = 2; i < argc; i++) {
314 if (strcasecmp(argv[i], "pass") == 0) {
315 if (++i >= argc) {
316 hlog(LOG_WARNING, "%s/%s: No passcode after pass command", c->addr_rem, username);
317 break;
318 }
319
320 if (!ssl_validated) {
321 given_passcode = atoi(argv[i]);
322 if (given_passcode >= 0)
323 if (given_passcode == aprs_passcode(c->username))
324 c->validated = VALIDATED_WEAK;
325 }
326 } else if (strcasecmp(argv[i], "vers") == 0) {
327 /* Collect application name and version separately.
328 * Some clients only give out application name but
329 * no version. If those same applications do try to
330 * use filter or udp, the filter/udp keyword will end
331 * up as the version number. So good luck with that.
332 */
333
334 if (i+1 >= argc) {
335 hlog(LOG_INFO, "%s/%s: No application name after 'vers' in login", c->addr_rem, username);
336 break;
337 }
338
339 login_set_app_name(c, argv[i+1], (i+2 < argc) ? argv[i+2] : "");
340 i += 2;
341
342 } else if (strcasecmp(argv[i], "udp") == 0) {
343 if (++i >= argc) {
344 hlog(LOG_WARNING, "%s/%s: Missing UDP port number after UDP command", c->addr_rem, username);
345 break;
346 }
347
348 int udp_port = atoi(argv[i]);
349 if (udp_port < 1024 || udp_port > 65535) {
350 hlog(LOG_WARNING, "%s/%s: UDP port number %s is out of range", c->addr_rem, username, argv[i]);
351 break;
352 }
353
354 if (login_setup_udp_feed(c, udp_port) != 0) {
355 /* Sorry, no UDP service for this port.. */
356 hlog(LOG_DEBUG, "%s/%s: Requested UDP on client port with no UDP configured", c->addr_rem, username);
357 rc = client_printf(self, c, "# No UDP service available on this port\r\n");
358 if (rc < -2)
359 return rc; // client got destroyed
360
361 }
362
363 } else if (strcasestr(argv[i], "filter")) {
364 /* Follows javaaprssrvr's example - any command having 'filter' in the
365 * end is OK. Case insensitive.
366 */
367 if (!(c->flags & CLFLAGS_USERFILTEROK)) {
368 rc = client_printf(self, c, "# No user-specified filters on this port\r\n");
369 if (rc < -2)
370 return rc; // client got destroyed
371 break;
372 }
373
374 /* copy the null-separated filter arguments back to a space-separated
375 * string, for the status page to show
376 */
377 char *fp = c->filter_s;
378 char *fe = c->filter_s + FILTER_S_SIZE;
379 int f_non_first = 0;
380
381 while (++i < argc) {
382 int l = strlen(argv[i]);
383 if (fp + l + 2 < fe) {
384 if (f_non_first) {
385 *fp++ = ' ';
386 }
387 memcpy(fp, argv[i], l);
388 fp += l;
389 *fp = 0;
390
391 f_non_first = 1;
392 }
393
394 /* parse filters in argv[i] */
395 rc = filter_parse(c, argv[i], 1);
396 if (rc) {
397 rc = client_printf( self, c, "# Parse errors on filter spec: '%s'\r\n", argv[i]);
398 if (rc < -2)
399 return rc; // The client probably got destroyed!
400 }
401 }
402 }
403 }
404
405 /* clean up the filter string so that it doesn't contain invalid
406 * UTF-8 or other binary stuff. */
407 sanitize_ascii_string(c->filter_s);
408
409 /* ok, login succeeded, switch handler */
410 c->handler_line_in = &incoming_handler; /* handler of all incoming APRS-IS data during a connection */
411
412 rc = client_printf( self, c, "# logresp %s %s, server %s\r\n",
413 username,
414 (c->validated) ? "verified" : "unverified",
415 serverid );
416 if (rc < -2)
417 return rc; // The client probably got destroyed!
418
419 hlog(LOG_DEBUG, "%s: login '%s'%s%s%s%s%s%s%s%s",
420 c->addr_rem, username,
421 (c->validated) ? " pass_ok" : "",
422 (!c->validated && given_passcode >= 0) ? " pass_invalid" : "",
423 (given_passcode < 0) ? " pass_none" : "",
424 (c->udp_port) ? " UDP" : "",
425 (*c->app_name) ? " app " : "",
426 (*c->app_name) ? c->app_name : "",
427 (*c->app_version) ? " ver " : "",
428 (*c->app_version) ? c->app_version : ""
429 );
430
431 /* mark as connected and classify */
432 worker_mark_client_connected(self, c);
433
434 /* Add the client to the client list.
435 *
436 * If the client logged in with a valid passcode, check if there are
437 * other validated clients logged in with the same username.
438 * If one is found, it needs to be disconnected.
439 *
440 * The lookup is done while holding the write lock to the clientlist,
441 * instead of a separate lookup call, so that two clients logging in
442 * at exactly the same time won't make it.
443 */
444
445 int old_fd = clientlist_add(c);
446 if (c->validated && old_fd != -1) {
447 /* TODO: If old connection is SSL validated, and this one is not, do not disconnect it. */
448 hlog(LOG_INFO, "fd %d: Disconnecting duplicate validated client with username '%s'", old_fd, username);
449 /* The other client may be on another thread, so cannot client_close() it.
450 * There is a small potential race here, if the old client disconnected and
451 * the fd was recycled for another client right after the clientlist check.
452 */
453 shutdown(old_fd, SHUT_RDWR);
454 }
455
456 return 0;
457
458 failed_login:
459
460 /* if we already lost the client, just return */
461 if (rc < -2)
462 return rc;
463
464 c->failed_cmds++;
465 if (c->failed_cmds >= 3) {
466 client_close(self, c, CLIERR_LOGIN_RETRIES);
467 return -3;
468 }
469
470 return rc;
471 }
472
473
474