void login_set_app_name(struct client_t *c, const char *app_name, const char *app_ver) { #ifndef FIXED_IOBUFS c->app_name = hstrdup(app_name); #else strncpy(c->app_name, app_name, sizeof(c->app_name)); c->app_name[sizeof(c->app_name)-1] = 0; #endif sanitize_ascii_string(c->app_name); #ifndef FIXED_IOBUFS c->app_version = hstrdup(app_ver); #else strncpy(c->app_version, app_ver, sizeof(c->app_version)); c->app_version[sizeof(c->app_version)-1] = 0; #endif sanitize_ascii_string(c->app_version); }
static void listener_copy_filters(struct listen_t *l, struct listen_config_t *lc) { int i; char filter_s[FILTER_S_SIZE] = ""; int filter_s_l = 0; for (i = 0; i < (sizeof(l->filters)/sizeof(l->filters[0])); ++i) { if (l->filters[i]) { hfree(l->filters[i]); l->filters[i] = NULL; } if (i < (sizeof(lc->filters)/sizeof(lc->filters[0]))) { if (!lc->filters[i]) continue; l->filters[i] = hstrdup(lc->filters[i]); int len = strlen(l->filters[i]); if (filter_s_l + len + 2 < FILTER_S_SIZE) { if (filter_s_l) filter_s[filter_s_l++] = ' '; memcpy(filter_s + filter_s_l, l->filters[i], len); filter_s_l += len; filter_s[filter_s_l] = 0; } } } if (l->filter_s) { hfree(l->filter_s); l->filter_s = NULL; } if (filter_s_l == 0) return; sanitize_ascii_string(filter_s); l->filter_s = hstrdup(filter_s); }
static int accept_liveupgrade_single(cJSON *client, int *rxerr_map, int rxerr_map_len) { cJSON *fd, *listener_id, *username, *time_connect, *tick_connect; cJSON *state; cJSON *addr_loc; cJSON *udp_port; cJSON *app_name, *app_version; cJSON *verified; cJSON *obuf_q; cJSON *bytes_rx, *bytes_tx; cJSON *pkts_rx, *pkts_tx, *pkts_ign; cJSON *rx_errs; cJSON *filter; cJSON *ibuf, *obuf; cJSON *client_heard; cJSON *lat, *lng; unsigned addr_len; union sockaddr_u sa; char *argv[256]; int i, argc; const char *username_s = "unknown"; /* get username first, so we can log it later */ username = accept_liveupgrade_cJSON_get(client, "username", cJSON_String, username_s); if (username) username_s = username->valuestring; fd = accept_liveupgrade_cJSON_get(client, "fd", cJSON_Number, username_s); int fd_i = -1; if (fd) fd_i = fd->valueint; if (fd_i < 0) { hlog(LOG_INFO, "Live upgrade: Client '%s' has negative fd %d, ignoring (corepeer?)", username_s, fd_i); return -1; } listener_id = accept_liveupgrade_cJSON_get(client, "listener_id", cJSON_Number, username_s); state = accept_liveupgrade_cJSON_get(client, "state", cJSON_String, username_s); time_connect = accept_liveupgrade_cJSON_get(client, "t_connect", cJSON_Number, username_s); addr_loc = accept_liveupgrade_cJSON_get(client, "addr_loc", cJSON_String, username_s); app_name = accept_liveupgrade_cJSON_get(client, "app_name", cJSON_String, username_s); app_version = accept_liveupgrade_cJSON_get(client, "app_version", cJSON_String, username_s); verified = accept_liveupgrade_cJSON_get(client, "verified", cJSON_Number, username_s); obuf_q = accept_liveupgrade_cJSON_get(client, "obuf_q", cJSON_Number, username_s); bytes_rx = accept_liveupgrade_cJSON_get(client, "bytes_rx", cJSON_Number, username_s); bytes_tx = accept_liveupgrade_cJSON_get(client, "bytes_tx", cJSON_Number, username_s); pkts_rx = accept_liveupgrade_cJSON_get(client, "pkts_rx", cJSON_Number, username_s); pkts_tx = accept_liveupgrade_cJSON_get(client, "pkts_tx", cJSON_Number, username_s); pkts_ign = accept_liveupgrade_cJSON_get(client, "pkts_ign", cJSON_Number, username_s); rx_errs = accept_liveupgrade_cJSON_get(client, "rx_errs", cJSON_Array, username_s); filter = accept_liveupgrade_cJSON_get(client, "filter", cJSON_String, username_s); /* optional */ tick_connect = cJSON_GetObjectItem(client, "t_connect_tick"); udp_port = cJSON_GetObjectItem(client, "udp_port"); ibuf = cJSON_GetObjectItem(client, "ibuf"); obuf = cJSON_GetObjectItem(client, "obuf"); client_heard = cJSON_GetObjectItem(client, "client_heard"); lat = cJSON_GetObjectItem(client, "lat"); lng = cJSON_GetObjectItem(client, "lng"); if (!( (fd) && (listener_id) && (state) && (username) && (time_connect) && (addr_loc) && (app_name) && (app_version) && (verified) && (obuf_q) && (bytes_rx) && (bytes_tx) && (pkts_rx) && (pkts_tx) && (pkts_ign) && (rx_errs) && (filter) )) { hlog(LOG_ERR, "Live upgrade: Fields missing from client JSON, discarding client fd %d", fd_i); if (fd_i >= 0) close(fd_i); return -1; } hlog(LOG_DEBUG, "Old client on fd %d: %s", fd->valueint, username->valuestring); /* fetch peer address from the fd instead of parsing it from text */ addr_len = sizeof(sa); if (getpeername(fd->valueint, &sa.sa, &addr_len) != 0) { /* Sometimes clients disconnect during upgrade, especially on slow RPi servers... */ if (errno == ENOTCONN) hlog(LOG_INFO, "Live upgrade: Client %s on fd %d has disconnected during upgrade (%s)", username->valuestring, fd->valueint, strerror(errno)); else hlog(LOG_ERR, "Live upgrade: getpeername client fd %d failed: %s", fd->valueint, strerror(errno)); close(fd->valueint); return -1; } /* convert client address to string */ char *client_addr_s = strsockaddr( &sa.sa, addr_len ); /* find the right listener for this client, for configuration and accounting */ struct listen_t *l = liveupgrade_find_listener(listener_id->valueint); if (!l) { hlog(LOG_INFO, "Live upgrade: Listener has been removed for fd %d (%s - local %s): disconnecting %s", fd->valueint, client_addr_s, addr_loc->valuestring, username->valuestring); close(fd->valueint); hfree(client_addr_s); return -1; } struct client_t *c = accept_client_for_listener(l, fd->valueint, client_addr_s, &sa, addr_len); if (!c) { hlog(LOG_ERR, "Live upgrade - client_alloc returned NULL, too many clients. Denied client %s on fd %d from %s", username->valuestring, fd->valueint, client_addr_s); close(fd->valueint); hfree(client_addr_s); return -1; } hfree(client_addr_s); if (strcmp(state->valuestring, "connected") == 0) { c->state = CSTATE_CONNECTED; c->handler_line_in = &incoming_handler; strncpy(c->username, username->valuestring, sizeof(c->username)); c->username[sizeof(c->username)-1] = 0; c->username_len = strlen(c->username); } else if (strcmp(state->valuestring, "login") == 0) { c->state = CSTATE_LOGIN; c->handler_line_in = &login_handler; } else { hlog(LOG_ERR, "Live upgrade: Client %s is in invalid state '%s' (fd %d)", l->addr_s, state->valuestring, l->fd); goto err; } /* distribute keepalive intervals for the existing old clients * but send them rather sooner than later */ // coverity[dont_call] // squelch warning: not security sensitive use of random(): load distribution c->keepalive = tick + (random() % (keepalive_interval/2)); /* distribute cleanup intervals over the next 2 minutes */ // coverity[dont_call] // squelch warning: not security sensitive use of random(): load distribution c->cleanup = tick + (random() % 120); c->connect_time = time_connect->valueint; /* live upgrade / backward compatibility: upgrading from <= 1.8.2 requires the 'else' path' */ if (tick_connect && tick_connect->type == cJSON_Number) c->connect_tick = tick_connect->valueint; else /* convert to monotonic time */ c->connect_tick = tick - (now - c->connect_time); c->validated = verified->valueint; c->localaccount.rxbytes = bytes_rx->valuedouble; c->localaccount.txbytes = bytes_tx->valuedouble; c->localaccount.rxpackets = pkts_rx->valuedouble; c->localaccount.txpackets = pkts_tx->valuedouble; c->localaccount.rxdrops = pkts_ign->valuedouble; login_set_app_name(c, app_name->valuestring, app_version->valuestring); // handle client's filter setting if (c->flags & CLFLAGS_USERFILTEROK && (filter) && (filter->valuestring) && *(filter->valuestring)) { // archive a copy of the filters, for status display strncpy(c->filter_s, filter->valuestring, FILTER_S_SIZE); c->filter_s[FILTER_S_SIZE-1] = 0; sanitize_ascii_string(c->filter_s); char *f = hstrdup(filter->valuestring); argc = parse_args(argv, f); for (i = 0; i < argc; ++i) { filter_parse(c, argv[i], 1); } hfree(f); } // set up UDP downstream if necessary if (udp_port && udp_port->type == cJSON_Number && udp_port->valueint > 1024 && udp_port->valueint < 65536) { if (login_setup_udp_feed(c, udp_port->valueint) != 0) { hlog(LOG_DEBUG, "%s/%s: Requested UDP on client port with no UDP configured", c->addr_rem, c->username); } } // fill up ibuf if (ibuf && ibuf->type == cJSON_String && ibuf->valuestring) { int l = hex_decode(c->ibuf, c->ibuf_size, ibuf->valuestring); if (l < 0) { hlog(LOG_ERR, "Live upgrade: %s/%s: Failed to decode ibuf: %s", c->addr_rem, c->username, ibuf->valuestring); } else { c->ibuf_end = l; hlog(LOG_DEBUG, "Live upgrade: Decoded ibuf %d bytes: '%.*s'", l, l, c->ibuf); hlog(LOG_DEBUG, "Hex: %s", ibuf->valuestring); } } // fill up obuf if (obuf && obuf->type == cJSON_String && obuf->valuestring) { int l = hex_decode(c->obuf, c->obuf_size, obuf->valuestring); if (l < 0) { hlog(LOG_ERR, "Live upgrade: %s/%s: Failed to decode obuf: %s", c->addr_rem, c->username, obuf->valuestring); } else { c->obuf_start = 0; c->obuf_end = l; hlog(LOG_DEBUG, "Live upgrade: Decoded obuf %d bytes: '%.*s'", l, l, c->obuf); hlog(LOG_DEBUG, "Hex: %s", obuf->valuestring); } } /* load list of stations heard by this client, to immediately support * messaging */ if (client_heard && client_heard->type == cJSON_Array) client_heard_json_load(c, client_heard); /* load rxerrs counters, with error name string mapping to support * adding/reordering of error counters */ if (rx_errs && rx_errs->type == cJSON_Array && rxerr_map && rxerr_map_len > 0) accept_rx_err_load(c, rx_errs, rxerr_map, rxerr_map_len); /* set client lat/lon, if they're given */ if (lat && lng && lat->type == cJSON_Number && lng->type == cJSON_Number) { c->loc_known = 1; c->lat = lat->valuedouble; c->lng = lng->valuedouble; } hlog(LOG_DEBUG, "%s - Accepted live upgrade client on fd %d from %s", c->addr_loc, c->fd, c->addr_rem); /* set client socket options, return -1 on serious errors */ if (set_client_sockopt(c) != 0) goto err; /* Add the client to the client list. */ int old_fd = clientlist_add(c); if (c->validated && old_fd != -1) { /* TODO: If old connection is SSL validated, and this one is not, do not disconnect it. */ hlog(LOG_INFO, "fd %d: Disconnecting duplicate validated client with username '%s'", old_fd, c->username); shutdown(old_fd, SHUT_RDWR); } /* ok, found it... lock the new client queue and pass the client */ if (pass_client_to_worker(pick_next_worker(), c)) goto err; return 0; err: close(c->fd); inbound_connects_account(0, c->portaccount); /* something failed, remove this from accounts.. */ client_free(c); return -1; }
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; } #ifndef FIXED_IOBUFS c->app_version = hstrdup(argv[++i]); #else strncpy(c->app_name, argv[++i], sizeof(c->app_name)); c->app_name[sizeof(c->app_name)-1] = 0; #endif sanitize_ascii_string(c->app_name); if (i+1 >= argc) { hlog(LOG_DEBUG, "%s/%s: No application version after 'vers' in login", c->addr_rem, username); break; } #ifndef FIXED_IOBUFS c->app_version = hstrdup(argv[++i]); #else strncpy(c->app_version, argv[++i], sizeof(c->app_version)); c->app_version[sizeof(c->app_version)-1] = 0; #endif sanitize_ascii_string(c->app_version); } 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; } c->udp_port = atoi(argv[i]); if (c->udp_port < 1024 || c->udp_port > 65535) { hlog(LOG_WARNING, "%s/%s: UDP port number %s is out of range", c->addr_rem, username, argv[i]); c->udp_port = 0; } if (c->udpclient) { c->udpaddr = c->addr; if (c->udpaddr.sa.sa_family == AF_INET) { c->udpaddr.si.sin_port = htons(c->udp_port); c->udpaddrlen = sizeof(c->udpaddr.si); } else { c->udpaddr.si6.sin6_port = htons(c->udp_port); c->udpaddrlen = sizeof(c->udpaddr.si6); } } else { /* 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); c->udp_port = 0; 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; 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_DEBUG, "fd %d: Disconnecting duplicate validated client with username '%s'", old_fd, username); 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) shutdown(c->fd, SHUT_RDWR); return rc; }