int uplink_login_handler(struct worker_t *self, struct client_t *c, int l4proto, char *s, int len) { char buf[1000]; int rc; int argc; char *argv[256]; hlog_packet(LOG_INFO, s, len, "%s: Uplink server software: ", c->addr_rem); #ifdef USE_SSL if (c->ssl_con && c->ssl_con->validate) { hlog(LOG_DEBUG, "%s/%s: Uplink: Validating SSL server cert against CA", c->addr_rem, c->username); int ssl_res = ssl_validate_peer_cert_phase1(c); if (ssl_res != 0) { hlog(LOG_WARNING, "%s/%s: SSL server cert validation failed: %s", c->addr_rem, c->username, ssl_strerror(ssl_res)); client_close(self, c, CLIERR_UPLINK_PEER_CERT_FAIL); return 0; } } #endif /* parse to arguments */ /* make it null-terminated for our string processing */ char *e = s + len; *e = 0; if ((argc = parse_args_noshell(argv, s)) == 0 || *argv[0] != '#') { hlog(LOG_ERR, "%s: Uplink's welcome message is not recognized: no # in beginning", c->addr_rem); client_close(self, c, CLIERR_UPLINK_LOGIN_PROTO_ERR); return 0; } if (argc >= 3) { strncpy(c->app_name, argv[1], sizeof(c->app_name)); c->app_name[sizeof(c->app_name)-1] = 0; strncpy(c->app_version, argv[2], sizeof(c->app_version)); c->app_version[sizeof(c->app_version)-1] = 0; } // TODO: The uplink login command here could maybe be improved to send a filter command. len = sprintf(buf, "user %s pass %s vers %s\r\n", serverid, passcode, verstr_aprsis); hlog(LOG_DEBUG, "%s: my login string: \"%.*s\"", c->addr_rem, len-2, buf, len); rc = c->write(self, c, buf, len); if (rc < -2) return rc; // the client was destroyed by client_write, don't touch it c->handler_line_in = uplink_logresp_handler; c->state = CSTATE_LOGRESP; hlog(LOG_INFO, "%s: Connected to server, logging in", c->addr_rem); return 0; }
int http_udp_upload_login(const char *addr_rem, char *s, char **username) { int argc; char *argv[256]; int i; int username_len; /* parse to arguments */ if ((argc = parse_args_noshell(argv, s)) == 0) return -1; if (argc < 2) { hlog(LOG_WARNING, "%s: HTTP POST: Invalid login string, too few arguments: '%s'", addr_rem, s); return -1; } if (strcasecmp(argv[0], "user") != 0) { hlog(LOG_WARNING, "%s: HTTP POST: Invalid login string, no 'user': '******'", addr_rem, s); return -1; } *username = argv[1]; username_len = strlen(*username); /* limit username length */ if (username_len > CALLSIGNLEN_MAX) { hlog(LOG_WARNING, "%s: HTTP POST: Invalid login string, too long 'user' username: '******'", addr_rem, *username); return -1; } /* check the username against a static list of disallowed usernames */ for (i = 0; (disallow_login_usernames[i]); i++) { if (strcasecmp(*username, disallow_login_usernames[i]) == 0) { hlog(LOG_WARNING, "%s: HTTP POST: Login by user '%s' not allowed", addr_rem, *username); return -1; } } /* make sure the callsign is OK on the APRS-IS */ if (check_invalid_q_callsign(*username, username_len)) { hlog(LOG_WARNING, "%s: HTTP POST: Invalid login string, invalid 'user': '******'", addr_rem, *username); return -1; } int given_passcode = -1; int validated = 0; for (i = 2; i < argc; i++) { if (strcasecmp(argv[i], "pass") == 0) { if (++i >= argc) { hlog(LOG_WARNING, "%s (%s): HTTP POST: No passcode after pass command", addr_rem, username); break; } given_passcode = atoi(argv[i]); if (given_passcode >= 0) if (given_passcode == aprs_passcode(*username)) validated = 1; } else if (strcasecmp(argv[i], "vers") == 0) { if (i+2 >= argc) { hlog(LOG_DEBUG, "%s (%s): HTTP POST: No application name and version after vers command", addr_rem, username); break; } // skip app name and version i += 2; } } return validated; }
int login_handler(struct worker_t *self, struct client_t *c, int l4proto, char *s, int len) { int argc; char *argv[256]; int i, rc; /* make it null-terminated for our string processing */ char *e = s + len; *e = 0; hlog(LOG_DEBUG, "%s: login string: '%s' (%d)", c->addr_rem, s, len); /* parse to arguments */ if ((argc = parse_args_noshell(argv, s)) == 0 || *argv[0] == '#') return 0; if (argc < 2) { hlog(LOG_WARNING, "%s: Invalid login string, too few arguments: '%s'", c->addr_rem, s); rc = client_printf(self, c, "# Invalid login string, too few arguments\r\n"); goto failed_login; } if (strcasecmp(argv[0], "user") != 0) { if (strcasecmp(argv[0], "GET") == 0) c->failed_cmds = 10; /* bail out right away for a HTTP client */ c->failed_cmds++; hlog(LOG_WARNING, "%s: Invalid login string, no 'user': '******'", c->addr_rem, s); rc = client_printf(self, c, "# Invalid login command\r\n"); goto failed_login; } char *username = argv[1]; /* limit username length */ if (strlen(username) > CALLSIGNLEN_MAX) { hlog(LOG_WARNING, "%s: Invalid login string, too long 'user' username: '******'", c->addr_rem, c->username); username[CALLSIGNLEN_MAX] = 0; rc = client_printf(self, c, "# Invalid username format\r\n"); goto failed_login; } #ifndef FIXED_IOBUFS c->username = hstrdup(username); #else strncpy(c->username, username, sizeof(c->username)); c->username[sizeof(c->username)-1] = 0; #endif c->username_len = strlen(c->username); /* check the username against a static list of disallowed usernames */ for (i = 0; (disallow_login_usernames[i]); i++) { if (strcasecmp(c->username, disallow_login_usernames[i]) == 0) { hlog(LOG_WARNING, "%s: Login by user '%s' not allowed", c->addr_rem, c->username); rc = client_printf(self, c, "# Login by user not allowed\r\n"); goto failed_login; } } /* make sure the callsign is OK on the APRS-IS */ if (check_invalid_q_callsign(c->username, c->username_len)) { hlog(LOG_WARNING, "%s: Invalid login string, invalid 'user': '******'", c->addr_rem, c->username); rc = client_printf(self, c, "# Invalid username format\r\n"); goto failed_login; } int given_passcode = -1; for (i = 2; i < argc; i++) { if (strcasecmp(argv[i], "pass") == 0) { if (++i >= argc) { hlog(LOG_WARNING, "%s/%s: No passcode after pass command", c->addr_rem, username); break; } given_passcode = atoi(argv[i]); if (given_passcode >= 0) if (given_passcode == aprs_passcode(c->username)) c->validated = 1; } else if (strcasecmp(argv[i], "vers") == 0) { /* Collect application name and version separately. * Some clients only give out application name but * no version. If those same applications do try to * use filter or udp, the filter/udp keyword will end * up as the version number. So good luck with that. */ if (i+1 >= argc) { hlog(LOG_INFO, "%s/%s: No application name after 'vers' in login", c->addr_rem, username); break; } login_set_app_name(c, argv[i+1], (i+2 < argc) ? argv[i+2] : ""); i += 2; } else if (strcasecmp(argv[i], "udp") == 0) { if (++i >= argc) { hlog(LOG_WARNING, "%s/%s: Missing UDP port number after UDP command", c->addr_rem, username); break; } int udp_port = atoi(argv[i]); if (udp_port < 1024 || udp_port > 65535) { hlog(LOG_WARNING, "%s/%s: UDP port number %s is out of range", c->addr_rem, username, argv[i]); break; } if (login_setup_udp_feed(c, udp_port) != 0) { /* Sorry, no UDP service for this port.. */ hlog(LOG_DEBUG, "%s/%s: Requested UDP on client port with no UDP configured", c->addr_rem, username); rc = client_printf(self, c, "# No UDP service available on this port\r\n"); if (rc < -2) return rc; // client got destroyed } } else if (strstr(argv[i], "filter")) { /* Follows javaaprssrvr's example - any command having 'filter' in the * end is OK. */ if (!(c->flags & CLFLAGS_USERFILTEROK)) { rc = client_printf(self, c, "# No user-specified filters on this port\r\n"); if (rc < -2) return rc; // client got destroyed break; } /* copy the null-separated filter arguments back to a space-separated * string, for the status page to show */ char *fp = c->filter_s; char *fe = c->filter_s + FILTER_S_SIZE; int f_non_first = 0; while (++i < argc) { int l = strlen(argv[i]); if (fp + l + 2 < fe) { if (f_non_first) { *fp++ = ' '; } memcpy(fp, argv[i], l); fp += l; *fp = 0; f_non_first = 1; } /* parse filters in argv[i] */ rc = filter_parse(c, argv[i], 1); if (rc) { rc = client_printf( self, c, "# Parse errors on filter spec: '%s'\r\n", argv[i]); if (rc < -2) return rc; // The client probably got destroyed! } } } } /* ok, login succeeded, switch handler */ c->handler = &incoming_handler; /* handler of all incoming APRS-IS data during a connection */ rc = client_printf( self, c, "# logresp %s %s, server %s\r\n", username, (c->validated) ? "verified" : "unverified", serverid ); if (rc < -2) return rc; // The client probably got destroyed! c->keepalive = now + keepalive_interval/2 + random() % keepalive_interval; c->state = CSTATE_CONNECTED; hlog(LOG_DEBUG, "%s: login '%s'%s%s%s%s%s%s%s%s", c->addr_rem, username, (c->validated) ? " pass_ok" : "", (!c->validated && given_passcode >= 0) ? " pass_invalid" : "", (given_passcode < 0) ? " pass_none" : "", (c->udp_port) ? " UDP" : "", (c->app_name) ? " app " : "", (c->app_name) ? c->app_name : "", (c->app_version) ? " ver " : "", (c->app_version) ? c->app_version : "" ); /* Add the client to the client list. * * If the client logged in with a valid passcode, check if there are * other validated clients logged in with the same username. * If one is found, it needs to be disconnected. * * The lookup is done while holding the write lock to the clientlist, * instead of a separate lookup call, so that two clients logging in * at exactly the same time won't make it. */ int old_fd = clientlist_add(c); if (c->validated && old_fd != -1) { hlog(LOG_INFO, "fd %d: Disconnecting duplicate validated client with username '%s'", old_fd, username); /* The other client may be on another thread, so cannot client_close() it. * There is a small potential race here, if the old client disconnected and * the fd was recycled for another client right after the clientlist check. */ shutdown(old_fd, SHUT_RDWR); } return 0; failed_login: /* if we already lost the client, just return */ if (rc < -2) return rc; c->failed_cmds++; if (c->failed_cmds >= 3) { client_close(self, c, CLIERR_LOGIN_RETRIES); return -3; } return rc; }
int uplink_logresp_handler(struct worker_t *self, struct client_t *c, int l4proto, char *s, int len) { int argc; char *argv[256]; char *p; hlog_packet(LOG_INFO, s, len, "%s: Uplink server login response: ", c->addr_rem); /* parse to arguments */ /* make it null-terminated for our string processing */ char *e = s + len; *e = 0; if ((argc = parse_args_noshell(argv, s)) == 0 || *argv[0] != '#') { hlog(LOG_ERR, "%s: Uplink's logresp message is not recognized: no # in beginning (protocol incompatibility)", c->addr_rem); client_close(self, c, CLIERR_UPLINK_LOGIN_PROTO_ERR); return 0; } if (argc < 6) { hlog(LOG_ERR, "%s: Uplink's logresp message does not have enough arguments (protocol incompatibility)", c->addr_rem); client_close(self, c, CLIERR_UPLINK_LOGIN_PROTO_ERR); return 0; } if (strcmp(argv[1], "logresp") != 0) { hlog(LOG_ERR, "%s: Uplink's logresp message does not say 'logresp' (protocol incompatibility)", c->addr_rem); client_close(self, c, CLIERR_UPLINK_LOGIN_PROTO_ERR); return 0; } if (strcmp(argv[2], serverid) != 0) { hlog(LOG_ERR, "%s: Uplink's logresp message does not have my callsign '%s' on it (protocol incompatibility)", c->addr_rem, serverid); client_close(self, c, CLIERR_UPLINK_LOGIN_PROTO_ERR); return 0; } if (strcmp(argv[3], "verified,") != 0) { hlog(LOG_ERR, "%s: Uplink's logresp message does not say I'm verified (wrong passcode in my configuration?)", c->addr_rem); client_close(self, c, CLIERR_UPLINK_LOGIN_NOT_VERIFIED); return 0; } if (strcmp(argv[4], "server") != 0) { hlog(LOG_ERR, "%s: Uplink's logresp message does not contain 'server' (protocol incompatibility)", c->addr_rem); client_close(self, c, CLIERR_UPLINK_LOGIN_PROTO_ERR); return 0; } p = strchr(argv[5], ','); if (p) *p = 0; if (strlen(argv[5]) > CALLSIGNLEN_MAX) { hlog(LOG_ERR, "%s: Uplink's server name is too long: '%s'", c->addr_rem, argv[5]); client_close(self, c, CLIERR_UPLINK_LOGIN_PROTO_ERR); return 0; } if (strcasecmp(argv[5], serverid) == 0) { hlog(LOG_ERR, "%s: Uplink's server name is same as ours: '%s'", c->addr_rem, argv[5]); client_close(self, c, CLIERR_UPLINK_LOGIN_PROTO_ERR); return 0; } /* todo: validate server callsign with the q valid path algorithm */ /* store the remote server's callsign as the "client username" */ strncpy(c->username, argv[5], sizeof(c->username)); c->username[sizeof(c->username)-1] = 0; /* uplink servers are always "validated" */ c->validated = VALIDATED_WEAK; /* check the server name against certificate */ #ifdef USE_SSL if (c->ssl_con && c->ssl_con->validate) { hlog(LOG_DEBUG, "%s/%s: Uplink: Validating SSL server cert subject", c->addr_rem, c->username); int ssl_res = ssl_validate_peer_cert_phase2(c); if (ssl_res != 0) { hlog(LOG_WARNING, "%s/%s: SSL server cert validation failed: %s", c->addr_rem, c->username, ssl_strerror(ssl_res)); client_close(self, c, CLIERR_UPLINK_PEER_CERT_FAIL); return 0; } c->validated = VALIDATED_STRONG; } #endif hlog(LOG_INFO, "%s: Uplink logged in to server %s", c->addr_rem, c->username); c->handler_line_in = incoming_handler; /* mark as connected and classify */ worker_mark_client_connected(self, c); return 0; }