/* * Copyright (C) 2014, 2015 Red Hat * * Author: Nikos Mavrogiannopoulos * * This file is part of ocserv. * * ocserv is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * ocserv is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hex.h" static int common_info_cmd(UserListRep *args, FILE *out, cmd_params_st *params); static int cookie_info_cmd(SecmListCookiesReplyMsg * args, FILE *out, cmd_params_st *params); struct unix_ctx { int fd; int is_open; const char *socket_file; }; static uint8_t msg_map[] = { [CTL_CMD_STATUS] = CTL_CMD_STATUS_REP, [CTL_CMD_RELOAD] = CTL_CMD_RELOAD_REP, [CTL_CMD_STOP] = CTL_CMD_STOP_REP, [CTL_CMD_LIST] = CTL_CMD_LIST_REP, [CTL_CMD_LIST_COOKIES] = CTL_CMD_LIST_COOKIES_REP, [CTL_CMD_LIST_BANNED] = CTL_CMD_LIST_BANNED_REP, [CTL_CMD_USER_INFO] = CTL_CMD_LIST_REP, [CTL_CMD_TOP] = CTL_CMD_LIST_REP, [CTL_CMD_ID_INFO] = CTL_CMD_LIST_REP, [CTL_CMD_DISCONNECT_NAME] = CTL_CMD_DISCONNECT_NAME_REP, [CTL_CMD_DISCONNECT_ID] = CTL_CMD_DISCONNECT_ID_REP, [CTL_CMD_UNBAN_IP] = CTL_CMD_UNBAN_IP_REP, }; struct cmd_reply_st { unsigned cmd; uint8_t *data; unsigned data_size; }; static void free_reply(struct cmd_reply_st *rep) { talloc_free(rep->data); } static void init_reply(struct cmd_reply_st *rep) { if (rep) rep->data = NULL; } /* sends a message and returns the reply */ static int send_cmd(struct unix_ctx *ctx, unsigned cmd, const void *data, pack_size_func get_size, pack_func pack, struct cmd_reply_st *rep) { uint8_t header[3]; struct iovec iov[2]; unsigned iov_len = 1; int e, ret; uint16_t rlength = 0; uint32_t length32 = 0; void *packed = NULL; if (get_size) rlength = get_size(data); header[0] = cmd; memcpy(&header[1], &rlength, 2); iov[0].iov_base = header; iov[0].iov_len = 3; if (data != NULL) { packed = talloc_size(ctx, rlength); if (packed == NULL) { fprintf(stderr, "memory error\n"); return -1; } iov[1].iov_base = packed; iov[1].iov_len = rlength; ret = pack(data, packed); if (ret == 0) { fprintf(stderr, "data packing error\n"); ret = -1; goto fail; } iov_len++; } ret = writev(ctx->fd, iov, iov_len); if (ret < 0) { e = errno; fprintf(stderr, "writev: %s\n", strerror(e)); ret = -1; goto fail; } if (rep != NULL) { ret = force_read_timeout(ctx->fd, header, 1+sizeof(length32), DEFAULT_TIMEOUT); if (ret == -1) { /*e = errno; fprintf(stderr, "read: %s\n", strerror(e));*/ ret = -1; goto fail; } if (ret != 1+sizeof(length32)) { fprintf(stderr, "short read %d\n", ret); ret = -1; goto fail; } rep->cmd = header[0]; if (msg_map[cmd] != rep->cmd) { fprintf(stderr, "Unexpected message '%d', expected '%d'\n", (int)rep->cmd, (int)msg_map[cmd]); ret = -1; goto fail; } memcpy(&length32, &header[1], 4); rep->data_size = length32; rep->data = talloc_size(ctx, length32); if (rep->data == NULL) { fprintf(stderr, "memory error\n"); ret = -1; goto fail; } ret = force_read_timeout(ctx->fd, rep->data, length32, DEFAULT_TIMEOUT); if (ret == -1) { e = errno; talloc_free(rep->data); rep->data = NULL; fprintf(stderr, "read: %s\n", strerror(e)); ret = -1; goto fail; } } ret = 0; fail: talloc_free(packed); return ret; } static int connect_to_ocserv (const char *socket_file) { int sd, ret, e; struct sockaddr_un sa; if (socket_file == NULL) socket_file = OCCTL_UNIX_SOCKET; memset(&sa, 0, sizeof(sa)); sa.sun_family = AF_UNIX; snprintf(sa.sun_path, sizeof(sa.sun_path), "%s", socket_file); sd = socket(AF_UNIX, SOCK_STREAM, 0); if (sd == -1) { e = errno; fprintf(stderr, "error opening socket: %s\n", strerror(e)); return -1; } ret = connect(sd, (struct sockaddr *)&sa, sizeof(sa)); if (ret == -1) { e = errno; fprintf(stderr, "error connecting to ocserv socket '%s': %s\n", sa.sun_path, strerror(e)); ret = -1; goto error; } return sd; error: close(sd); return ret; } int handle_status_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_st *params) { int ret; struct cmd_reply_st raw; StatusRep *rep; char str_since[64]; char buf[MAX_TMPSTR_SIZE]; time_t t; struct tm *tm; PROTOBUF_ALLOCATOR(pa, ctx); init_reply(&raw); print_start_block(stdout, params); if (NO_JSON(params)) printf("OpenConnect SSL VPN server\n"); ret = send_cmd(ctx, CTL_CMD_STATUS, NULL, NULL, NULL, &raw); if (ret < 0) { goto error_status; } rep = status_rep__unpack(&pa, raw.data_size, raw.data); if (rep == NULL) goto error_status; print_single_value(stdout, params, "Status", rep->status != 0 ? "online" : "error", 1); t = rep->start_time; tm = localtime(&t); print_time_ival7(buf, time(0), t); strftime(str_since, sizeof(str_since), DATE_TIME_FMT, tm); print_single_value_ex(stdout, params, "Up since", str_since, buf, 1); print_single_value_int(stdout, params, "Clients", rep->active_clients, 1); print_single_value_int(stdout, params, "Sec-mod client entries", rep->secmod_client_entries, 1); print_single_value_int(stdout, params, "IPs in ban list", rep->banned_ips, 1); print_single_value_int(stdout, params, "TLS DB entries", rep->stored_tls_sessions, 1); print_separator(stdout, params); print_single_value_int(stdout, params, "Server PID", rep->pid, 1); print_single_value_int(stdout, params, "Sec-mod PID", rep->sec_mod_pid, 0); print_end_block(stdout, params, 0); status_rep__free_unpacked(rep, &pa); ret = 0; goto cleanup; error_status: print_single_value(stdout, params, "Status", "offline", 0); print_end_block(stdout, params, 0); ret = 1; cleanup: free_reply(&raw); return ret; } int handle_reload_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_st *params) { int ret; struct cmd_reply_st raw; BoolMsg *rep; unsigned status; PROTOBUF_ALLOCATOR(pa, ctx); init_reply(&raw); ret = send_cmd(ctx, CTL_CMD_RELOAD, NULL, NULL, NULL, &raw); if (ret < 0) { goto error_status; } rep = bool_msg__unpack(&pa, raw.data_size, raw.data); if (rep == NULL) goto error_status; status = rep->status; bool_msg__free_unpacked(rep, &pa); if (status != 0) printf("Server scheduled to reload\n"); else goto error_status; ret = 0; goto cleanup; error_status: printf("Error scheduling reload\n"); ret = 1; cleanup: free_reply(&raw); return ret; } int handle_stop_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_st *params) { int ret; struct cmd_reply_st raw; BoolMsg *rep; unsigned status; PROTOBUF_ALLOCATOR(pa, ctx); init_reply(&raw); ret = send_cmd(ctx, CTL_CMD_STOP, NULL, NULL, NULL, &raw); if (ret < 0) { goto error_status; } rep = bool_msg__unpack(&pa, raw.data_size, raw.data); if (rep == NULL) goto error_status; status = rep->status; bool_msg__free_unpacked(rep, &pa); if (status != 0) printf("Server scheduled to stop\n"); else goto error_status; ret = 0; goto cleanup; error_status: printf("Error scheduling server stop\n"); ret = 1; goto cleanup; cleanup: free_reply(&raw); return ret; } int handle_unban_ip_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_st *params) { int ret; struct cmd_reply_st raw; BoolMsg *rep; unsigned status; UnbanReq req = UNBAN_REQ__INIT; int af; unsigned char tmp[16]; PROTOBUF_ALLOCATOR(pa, ctx); if (arg == NULL || need_help(arg)) { check_cmd_help(rl_line_buffer); return 1; } init_reply(&raw); /* convert the IP to the simplest form */ if (strchr(arg, ':') != 0) { af = AF_INET6; } else { af = AF_INET; } ret = inet_pton(af, arg, tmp); if (ret == 1) { req.ip.data = tmp; if (af == AF_INET) req.ip.len = 4; else req.ip.len = 16; } else { fprintf(stderr, "Cannot parse IP: %s", arg); return 1; } ret = send_cmd(ctx, CTL_CMD_UNBAN_IP, &req, (pack_size_func)unban_req__get_packed_size, (pack_func)unban_req__pack, &raw); if (ret < 0) { goto error; } rep = bool_msg__unpack(&pa, raw.data_size, raw.data); if (rep == NULL) goto error; status = rep->status; bool_msg__free_unpacked(rep, &pa); if (status != 0) { printf("IP '%s' was unbanned\n", arg); } else { printf("could not unban IP '%s'\n", arg); } ret = 0; goto cleanup; error: fprintf(stderr, ERR_SERVER_UNREACHABLE); ret = 1; cleanup: free_reply(&raw); return ret; } int handle_disconnect_user_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_st *params) { int ret; struct cmd_reply_st raw; BoolMsg *rep; unsigned status; UsernameReq req = USERNAME_REQ__INIT; PROTOBUF_ALLOCATOR(pa, ctx); if (arg == NULL || need_help(arg)) { check_cmd_help(rl_line_buffer); return 1; } init_reply(&raw); req.username = (void*)arg; ret = send_cmd(ctx, CTL_CMD_DISCONNECT_NAME, &req, (pack_size_func)username_req__get_packed_size, (pack_func)username_req__pack, &raw); if (ret < 0) { goto error; } rep = bool_msg__unpack(&pa, raw.data_size, raw.data); if (rep == NULL) goto error; status = rep->status; bool_msg__free_unpacked(rep, &pa); if (status != 0) { printf("user '%s' was disconnected\n", arg); } else { printf("could not disconnect user '%s'\n", arg); } ret = 0; goto cleanup; error: fprintf(stderr, ERR_SERVER_UNREACHABLE); ret = 1; cleanup: free_reply(&raw); return ret; } int handle_disconnect_id_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_st *params) { int ret; struct cmd_reply_st raw; BoolMsg *rep; unsigned status; unsigned id; IdReq req = ID_REQ__INIT; PROTOBUF_ALLOCATOR(pa, ctx); if (arg != NULL) id = atoi(arg); if (arg == NULL || need_help(arg) || id == 0) { check_cmd_help(rl_line_buffer); return 1; } init_reply(&raw); req.id = id; ret = send_cmd(ctx, CTL_CMD_DISCONNECT_ID, &req, (pack_size_func)id_req__get_packed_size, (pack_func)id_req__pack, &raw); if (ret < 0) { goto error; } rep = bool_msg__unpack(&pa, raw.data_size, raw.data); if (rep == NULL) goto error; status = rep->status; bool_msg__free_unpacked(rep, &pa); if (status != 0) { printf("connection ID '%s' was disconnected\n", arg); ret = 0; } else { printf("could not disconnect ID '%s'\n", arg); ret = 1; } goto cleanup; error: fprintf(stderr, ERR_SERVER_UNREACHABLE); ret = 1; cleanup: free_reply(&raw); return ret; } static const char *fix_ciphersuite(char *txt) { if (txt != NULL && txt[0] != 0) { if (strlen(txt) > 16 && strncmp(txt, "(DTLS", 5) == 0 && strncmp(&txt[8], ")-(RSA)-", 8) == 0) { return txt + 16; } } return "(no-dtls)"; } static const char *get_ip(const char *ip1, const char *ip2) { if (ip1 != NULL && ip1[0] != 0) return ip1; else return ip2; } void common_user_list(struct unix_ctx *ctx, UserListRep *rep, FILE *out, cmd_params_st *params) { unsigned i; const char *vpn_ip, *groupname, *username; const char *dtls_ciphersuite; char tmpbuf[MAX_TMPSTR_SIZE]; time_t t; struct tm *tm; char str_since[64]; if (HAVE_JSON(params)) { common_info_cmd(rep, out, params); } else for (i=0;in_user;i++) { username = rep->user[i]->username; if (username == NULL || username[0] == 0) username = NO_USER; vpn_ip = get_ip(rep->user[i]->local_ip, rep->user[i]->local_ip6); /* add header */ if (i == 0) { fprintf(out, "%8s %8s %8s %14s %14s %6s %7s %14s %9s\n", "id", "user", "group", "ip", "vpn-ip", "device", "since", "dtls-cipher", "status"); } t = rep->user[i]->conn_time; tm = localtime(&t); strftime(str_since, sizeof(str_since), DATE_TIME_FMT, tm); groupname = rep->user[i]->groupname; if (groupname == NULL || groupname[0] == 0) groupname = NO_GROUP; print_time_ival7(tmpbuf, time(0), t); fprintf(out, "%8d %8s %8s %14s %14s %6s ", (int)rep->user[i]->id, username, groupname, rep->user[i]->ip, vpn_ip, rep->user[i]->tun); dtls_ciphersuite = fix_ciphersuite(rep->user[i]->dtls_ciphersuite); fprintf(out, "%s %14s %9s\n", tmpbuf, dtls_ciphersuite, rep->user[i]->status); entries_add(ctx, username, strlen(username), rep->user[i]->id); } } int handle_list_users_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_st *params) { int ret; struct cmd_reply_st raw; UserListRep *rep = NULL; FILE *out; PROTOBUF_ALLOCATOR(pa, ctx); init_reply(&raw); entries_clear(); out = pager_start(params); ret = send_cmd(ctx, CTL_CMD_LIST, NULL, NULL, NULL, &raw); if (ret < 0) { goto error; } rep = user_list_rep__unpack(&pa, raw.data_size, raw.data); if (rep == NULL) goto error; common_user_list(ctx, rep, out, params); ret = 0; goto cleanup; error: ret = 1; fprintf(stderr, ERR_SERVER_UNREACHABLE); cleanup: if (rep != NULL) user_list_rep__free_unpacked(rep, &pa); free_reply(&raw); pager_stop(out); return ret; } void cookie_list(struct unix_ctx *ctx, SecmListCookiesReplyMsg *rep, FILE *out, cmd_params_st *params) { unsigned i; const char *groupname, *username; char tmpbuf[MAX_TMPSTR_SIZE]; time_t t; struct tm *tm; char str_since[65]; if (HAVE_JSON(params)) { cookie_info_cmd(rep, out, params); } else for (i=0;in_cookies;i++) { username = rep->cookies[i]->username; if (username == NULL || username[0] == 0) username = NO_USER; /* add header */ if (i == 0) { fprintf(out, "%6s %8s %8s %14s %24s %8s %8s\n", "cookie", "user", "group", "ip", "user agent", "updated", "status"); } t = rep->cookies[i]->last_modified; tm = localtime(&t); strftime(str_since, sizeof(str_since), DATE_TIME_FMT, tm); groupname = rep->cookies[i]->groupname; if (groupname == NULL || groupname[0] == 0) groupname = NO_GROUP; print_time_ival7(tmpbuf, time(0), t); fprintf(out, "%.6s %8s %8s %14s %.24s %8s %8s\n", rep->cookies[i]->psid, username, groupname, rep->cookies[i]->remote_ip, rep->cookies[i]->user_agent, tmpbuf, rep->cookies[i]->status); } } int handle_list_cookies_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_st *params) { int ret; struct cmd_reply_st raw; SecmListCookiesReplyMsg *rep = NULL; FILE *out; PROTOBUF_ALLOCATOR(pa, ctx); init_reply(&raw); entries_clear(); out = pager_start(params); ret = send_cmd(ctx, CTL_CMD_LIST_COOKIES, NULL, NULL, NULL, &raw); if (ret < 0) { goto error; } rep = secm_list_cookies_reply_msg__unpack(&pa, raw.data_size, raw.data); if (rep == NULL) goto error; cookie_list(ctx, rep, out, params); ret = 0; goto cleanup; error: ret = 1; fprintf(stderr, ERR_SERVER_UNREACHABLE); cleanup: if (rep != NULL) secm_list_cookies_reply_msg__free_unpacked(rep, &pa); free_reply(&raw); pager_stop(out); return ret; } int handle_list_iroutes_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_st *params) { int ret; struct cmd_reply_st raw; UserListRep *rep = NULL; FILE *out; unsigned i, j; PROTOBUF_ALLOCATOR(pa, ctx); init_reply(&raw); entries_clear(); out = pager_start(params); /* get all user info */ ret = send_cmd(ctx, CTL_CMD_LIST, NULL, NULL, NULL, &raw); if (ret < 0) { goto error; } rep = user_list_rep__unpack(&pa, raw.data_size, raw.data); if (rep == NULL) goto error; /* print iroutes */ if (NO_JSON(params)) { for (i=0;in_user;i++) { const char *username, *vpn_ip; username = rep->user[i]->username; if (username == NULL || username[0] == 0) username = NO_USER; vpn_ip = get_ip(rep->user[i]->local_ip, rep->user[i]->local_ip6); /* add header */ if (i == 0) { fprintf(out, "%6s %8s %6s %16s %28s\n", "id", "user", "device", "vpn-ip", "iroute"); } for (j=0;juser[i]->n_iroutes;j++) fprintf(out, "%6d %8s %6s %16s %28s", (int)rep->user[i]->id, username, rep->user[i]->tun, vpn_ip, rep->user[i]->iroutes[j]); } } else { print_start_block(out, params); for (i=0;in_user;i++) { const char *username, *vpn_ip; username = rep->user[i]->username; if (username == NULL || username[0] == 0) username = NO_USER; vpn_ip = get_ip(rep->user[i]->local_ip, rep->user[i]->local_ip6); print_single_value_int(out, params, "ID", rep->user[i]->id, 1); print_single_value(out, params, "Username", username, 1); print_single_value(out, params, "Device", rep->user[i]->tun, 1); print_single_value(out, params, "IP", vpn_ip, 1); print_list_entries(out, params, "iRoutes", rep->user[i]->iroutes, rep->user[i]->n_iroutes, 1); print_single_value(out, params, "IP", vpn_ip, 0); } print_end_block(out, params, 0); } ret = 0; goto cleanup; error: ret = 1; fprintf(stderr, ERR_SERVER_UNREACHABLE); cleanup: if (rep != NULL) user_list_rep__free_unpacked(rep, &pa); free_reply(&raw); pager_stop(out); return ret; } static int handle_list_banned_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_st *params, unsigned points) { int ret; struct cmd_reply_st raw; BanListRep *rep = NULL; unsigned i; char str_since[64]; char tmpbuf[MAX_TMPSTR_SIZE]; FILE *out; struct tm *tm; time_t t; PROTOBUF_ALLOCATOR(pa, ctx); char txt_ip[MAX_IP_STR]; const char *tmp_str; init_reply(&raw); ip_entries_clear(); out = pager_start(params); ret = send_cmd(ctx, CTL_CMD_LIST_BANNED, NULL, NULL, NULL, &raw); if (ret < 0) { goto error; } rep = ban_list_rep__unpack(&pa, raw.data_size, raw.data); if (rep == NULL) goto error; print_array_block(out, params); for (i=0;in_info;i++) { if (rep->info[i]->ip.len < 4) continue; if (rep->info[i]->ip.len == 16) tmp_str = inet_ntop(AF_INET6, rep->info[i]->ip.data, txt_ip, sizeof(txt_ip)); else tmp_str = inet_ntop(AF_INET, rep->info[i]->ip.data, txt_ip, sizeof(txt_ip)); if (tmp_str == NULL) strlcpy(txt_ip, "(unknown)", sizeof(txt_ip)); /* add header */ if (points == 0) { if (rep->info[i]->has_expires) { t = rep->info[i]->expires; tm = localtime(&t); strftime(str_since, sizeof(str_since), DATE_TIME_FMT, tm); } else { continue; } if (i == 0 && NO_JSON(params)) { fprintf(out, "%14s %14s %30s\n", "IP", "score", "expires"); } print_start_block(out, params); print_time_ival7(tmpbuf, t, time(0)); if (HAVE_JSON(params)) { print_single_value(out, params, "IP", txt_ip, 1); print_single_value_ex(out, params, "Since", str_since, tmpbuf, 1); print_single_value_int(out, params, "Score", rep->info[i]->score, 0); } else { fprintf(out, "%14s %14u %30s (%s)\n", txt_ip, (unsigned)rep->info[i]->score, str_since, tmpbuf); } } else { if (i == 0 && NO_JSON(params)) { fprintf(out, "%14s %14s\n", "IP", "score"); } print_start_block(out, params); if (HAVE_JSON(params)) { print_single_value(out, params, "IP", txt_ip, 1); print_single_value_int(out, params, "Score", rep->info[i]->score, 0); } else { fprintf(out, "%14s %14u\n", txt_ip, (unsigned)rep->info[i]->score); } } print_end_block(out, params, i<(rep->n_info-1)?1:0); ip_entries_add(ctx, txt_ip, strlen(txt_ip)); } print_end_array_block(out, params); ret = 0; goto cleanup; error: ret = 1; fprintf(stderr, ERR_SERVER_UNREACHABLE); cleanup: if (rep != NULL) ban_list_rep__free_unpacked(rep, &pa); free_reply(&raw); pager_stop(out); return ret; } int handle_list_banned_ips_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_st *params) { return handle_list_banned_cmd(ctx, arg, params, 0); } int handle_list_banned_points_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_st *params) { return handle_list_banned_cmd(ctx, arg, params, 1); } static char *int2str(char tmpbuf[MAX_TMPSTR_SIZE], int i) { tmpbuf[0] = 0; snprintf(tmpbuf, MAX_TMPSTR_SIZE, "%d", i); return tmpbuf; } static int common_info_cmd(UserListRep * args, FILE *out, cmd_params_st *params) { char *username = ""; char *groupname = ""; char str_since[64]; char tmpbuf[MAX_TMPSTR_SIZE]; char tmpbuf2[MAX_TMPSTR_SIZE]; struct tm *tm; time_t t; unsigned at_least_one = 0; int ret = 1, r; unsigned i; unsigned init_pager = 0; if (out == NULL) { out = pager_start(params); init_pager = 1; } if (HAVE_JSON(params)) fprintf(out, "[\n"); for (i=0;in_user;i++) { if (at_least_one > 0) fprintf(out, "\n"); print_start_block(out, params); print_single_value_int(out, params, "ID", args->user[i]->id, 1); t = args->user[i]->conn_time; tm = localtime(&t); strftime(str_since, sizeof(str_since), DATE_TIME_FMT, tm); username = args->user[i]->username; if (username == NULL || username[0] == 0) username = NO_USER; groupname = args->user[i]->groupname; if (groupname == NULL || groupname[0] == 0) groupname = NO_GROUP; print_pair_value(out, params, "Username", username, "Groupname", groupname, 1); print_single_value(out, params, "State", args->user[i]->status, 1); if (args->user[i]->has_mtu != 0) print_pair_value(out, params, "Device", args->user[i]->tun, "MTU", int2str(tmpbuf, args->user[i]->mtu), 1); else print_single_value(out, params, "Device", args->user[i]->tun, 1); print_pair_value(out, params, "Remote IP", args->user[i]->ip, "Local Device IP", args->user[i]->local_dev_ip, 1); if (args->user[i]->local_ip != NULL && args->user[i]->local_ip[0] != 0 && args->user[i]->remote_ip != NULL && args->user[i]->remote_ip[0] != 0) { print_pair_value(out, params, "IPv4", args->user[i]->local_ip, "P-t-P IPv4", args->user[i]->remote_ip, 1); } if (args->user[i]->local_ip6 != NULL && args->user[i]->local_ip6[0] != 0 && args->user[i]->remote_ip6 != NULL && args->user[i]->remote_ip6[0] != 0) { print_pair_value(out, params, "IPv6", args->user[i]->local_ip6, "P-t-P IPv6", args->user[i]->remote_ip6, 1); } print_single_value(out, params, "User-Agent", args->user[i]->user_agent, 1); if (args->user[i]->rx_per_sec > 0 || args->user[i]->tx_per_sec > 0) { /* print limits */ char buf1[32]; char buf2[32]; if (args->user[i]->rx_per_sec > 0 && args->user[i]->tx_per_sec > 0) { bytes2human(args->user[i]->rx_per_sec, buf1, sizeof(buf1), "/sec"); bytes2human(args->user[i]->tx_per_sec, buf2, sizeof(buf2), "/sec"); print_pair_value(out, params, "Limit RX", buf1, "TX", buf2, 1); } else if (args->user[i]->tx_per_sec > 0) { bytes2human(args->user[i]->tx_per_sec, buf1, sizeof(buf1), "/sec"); print_single_value(out, params, "Limit TX", buf1, 1); } else if (args->user[i]->rx_per_sec > 0) { bytes2human(args->user[i]->rx_per_sec, buf1, sizeof(buf1), "/sec"); print_single_value(out, params, "Limit RX", buf1, 1); } } print_iface_stats(args->user[i]->tun, args->user[i]->conn_time, out, params, 1); print_pair_value(out, params, "DPD", int2str(tmpbuf, args->user[i]->dpd), "KeepAlive", int2str(tmpbuf2, args->user[i]->keepalive), 1); print_single_value(out, params, "Hostname", args->user[i]->hostname, 1); print_time_ival7(tmpbuf, time(0), t); print_single_value_ex(out, params, "Connected at", str_since, tmpbuf, 1); print_single_value(out, params, "TLS ciphersuite", args->user[i]->tls_ciphersuite, 1); print_single_value(out, params, "DTLS cipher", args->user[i]->dtls_ciphersuite, 1); print_pair_value(out, params, "CSTP compression", args->user[i]->cstp_compr, "DTLS compression", args->user[i]->dtls_compr, 1); print_separator(out, params); /* user network info */ if (print_list_entries(out, params, "DNS", args->user[i]->dns, args->user[i]->n_dns, 1) < 0) goto error_parse; if (print_list_entries(out, params, "NBNS", args->user[i]->nbns, args->user[i]->n_nbns, 1) < 0) goto error_parse; if (print_list_entries(out, params, "Split-DNS-Domains", args->user[i]->domains, args->user[i]->n_domains, 1) < 0) goto error_parse; if ((r = print_list_entries(out, params, "Routes", args->user[i]->routes, args->user[i]->n_routes, 1)) < 0) goto error_parse; if (r == 0) { print_single_value(out, params, "Routes", "defaultroute", 1); } if ((r = print_list_entries(out, params, "No-routes", args->user[i]->no_routes, args->user[i]->n_no_routes, 1)) < 0) goto error_parse; if (print_list_entries(out, params, "iRoutes", args->user[i]->iroutes, args->user[i]->n_iroutes, 1) < 0) goto error_parse; print_single_value(out, params, "Restricted to routes", args->user[i]->restrict_to_routes?"True":"False", 1); if (print_fwport_entries(out, params, "Restricted to ports", args->user[i]->fw_ports, args->user[i]->n_fw_ports, 0) < 0) goto error_parse; print_end_block(out, params, i<(args->n_user-1)?1:0); at_least_one = 1; } if (HAVE_JSON(params)) fprintf(out, "]\n"); ret = 0; goto cleanup; error_parse: fprintf(stderr, "%s: message parsing error\n", __func__); goto cleanup; cleanup: if (at_least_one == 0) { if (NO_JSON(params)) fprintf(out, "user or ID not found\n"); ret = 2; } if (init_pager) pager_stop(out); return ret; } static int cookie_info_cmd(SecmListCookiesReplyMsg * args, FILE *out, cmd_params_st *params) { char *username = ""; char *groupname = ""; char str_since[65]; struct tm *tm; time_t t; unsigned at_least_one = 0; int ret = 1; unsigned i; unsigned init_pager = 0; if (out == NULL) { out = pager_start(params); init_pager = 1; } if (HAVE_JSON(params)) fprintf(out, "[\n"); for (i=0;in_cookies;i++) { if (at_least_one > 0) fprintf(out, "\n"); print_start_block(out, params); print_single_value_int(out, params, "session_is_open", args->cookies[i]->session_is_open, 1); print_single_value_int(out, params, "tls_auth_ok", args->cookies[i]->tls_auth_ok, 1); print_single_value(out, params, "State", args->cookies[i]->status, 1); t = args->cookies[i]->last_modified; tm = localtime(&t); strftime(str_since, sizeof(str_since), DATE_TIME_FMT, tm); username = args->cookies[i]->username; if (username == NULL || username[0] == 0) username = NO_USER; groupname = args->cookies[i]->groupname; if (groupname == NULL || groupname[0] == 0) groupname = NO_GROUP; print_pair_value(out, params, "Username", username, "Groupname", groupname, 1); print_single_value(out, params, "User-Agent", args->cookies[i]->user_agent, 1); print_single_value(out, params, "Remote IP", args->cookies[i]->remote_ip, 1); print_single_value(out, params, "Last Modified", str_since, 1); str_since[0] = 0; hex_encode(args->cookies[i]->sid.data, args->cookies[i]->sid.len, str_since, sizeof(str_since)); print_single_value(out, params, "cookie", str_since, 1); print_single_value(out, params, "Printable cookie", args->cookies[i]->psid, 1); print_single_value(out, params, "Last Modified", str_since, 0); print_end_block(out, params, i<(args->n_cookies-1)?1:0); at_least_one = 1; } if (HAVE_JSON(params)) fprintf(out, "]\n"); ret = 0; goto cleanup; cleanup: if (at_least_one == 0) { if (NO_JSON(params)) fprintf(out, "user or ID not found\n"); ret = 2; } if (init_pager) pager_stop(out); return ret; } int handle_show_user_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_st *params) { int ret; struct cmd_reply_st raw; UserListRep *rep = NULL; UsernameReq req = USERNAME_REQ__INIT; PROTOBUF_ALLOCATOR(pa, ctx); if (arg == NULL || need_help(arg)) { check_cmd_help(rl_line_buffer); return 1; } init_reply(&raw); req.username = (void*)arg; ret = send_cmd(ctx, CTL_CMD_USER_INFO, &req, (pack_size_func)username_req__get_packed_size, (pack_func)username_req__pack, &raw); if (ret < 0) { goto error; } rep = user_list_rep__unpack(&pa, raw.data_size, raw.data); if (rep == NULL) goto error; ret = common_info_cmd(rep, NULL, params); if (ret < 0) goto error; goto cleanup; error: fprintf(stderr, ERR_SERVER_UNREACHABLE); ret = 1; cleanup: if (rep != NULL) user_list_rep__free_unpacked(rep, &pa); free_reply(&raw); return ret; } static void dummy_sighandler(int signo) { return; } int handle_events_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_st *params) { uint8_t header[3]; int ret; struct cmd_reply_st raw; UserListRep *rep1 = NULL; TopUpdateRep *rep2 = NULL; uint32_t slength; unsigned data_size; uint8_t *data = NULL; char *groupname; char tmpbuf[MAX_TMPSTR_SIZE]; PROTOBUF_ALLOCATOR(pa, ctx); struct termios tio_old, tio_new; SIGHANDLER_T old_sighandler; init_reply(&raw); ret = send_cmd(ctx, CTL_CMD_TOP, NULL, 0, 0, &raw); if (ret < 0) { goto error; } rep1 = user_list_rep__unpack(&pa, raw.data_size, raw.data); if (rep1 == NULL) goto error; common_user_list(ctx, rep1, stdout, params); user_list_rep__free_unpacked(rep1, &pa); rep1 = NULL; fputs("\n", stdout); fputs("Press 'q' or CTRL+C to quit\n\n", stdout); old_sighandler = ocsignal(SIGINT, dummy_sighandler); tcgetattr(STDIN_FILENO, &tio_old); tio_new = tio_old; tio_new.c_lflag &= ~(ICANON|ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &tio_new); /* start listening for updates */ while(1) { fd_set rfds; FD_ZERO(&rfds); FD_SET(STDIN_FILENO, &rfds); FD_SET(ctx->fd, &rfds); ret = select(MAX(STDIN_FILENO,ctx->fd)+1, &rfds, NULL, NULL, NULL); if (ret == -1 && errno == EINTR) { ret = 0; break; } if (ret == -1) { int e = errno; fprintf(stderr, "events: select: %s\n", strerror(e)); ret = -1; break; } if (FD_ISSET(STDIN_FILENO, &rfds)) { ret = getchar(); if (ret == 'q' || ret == 'Q') { ret = 0; break; } } if (!FD_ISSET(ctx->fd, &rfds)) continue; ret = force_read_timeout(ctx->fd, header, 1+sizeof(slength), DEFAULT_TIMEOUT); if (ret == -1) { int e = errno; fprintf(stderr, "events: read1: %s\n", strerror(e)); ret = -1; break; } if (ret == 0) { fprintf(stderr, "events: server closed the connection\n"); ret = 0; break; } if (ret != 1+sizeof(slength)) { fprintf(stderr, "events: short read %d\n", ret); ret = -1; break; } if (header[0] != CTL_CMD_TOP_UPDATE_REP) { fprintf(stderr, "events: Unexpected message '%d', expected '%d'\n", (int)header[0], (int)CTL_CMD_TOP_UPDATE_REP); ret = -1; break; } memcpy(&slength, &header[1], sizeof(slength)); data_size = slength; data = talloc_size(ctx, slength); if (data == NULL) { fprintf(stderr, "events: memory error\n"); ret = -1; break; } ret = force_read(ctx->fd, data, data_size); if (ret == -1) { int e = errno; fprintf(stderr, "events: read: %s\n", strerror(e)); ret = -1; break; } /* parse and print */ rep2 = top_update_rep__unpack(&pa, data_size, data); if (rep2 == NULL) goto error; if (HAVE_JSON(params)) { if (rep2->connected == 0) rep2->user->user[0]->status = talloc_strdup(rep2, "disconnected"); else rep2->user->user[0]->status = talloc_strdup(rep2, "connected"); common_info_cmd(rep2->user, stdout, params); } else { groupname = rep2->user->user[0]->groupname; if (groupname == NULL || groupname[0] == 0) groupname = NO_GROUP; if (rep2->connected) { printf("connected user '%s' (%u) from %s with IP %s\n", rep2->user->user[0]->username, rep2->user->user[0]->id, rep2->user->user[0]->ip, get_ip(rep2->user->user[0]->local_ip, rep2->user->user[0]->local_ip6)); entries_add(ctx, rep2->user->user[0]->username, strlen(rep2->user->user[0]->username), rep2->user->user[0]->id); } else { print_time_ival7(tmpbuf, time(0), rep2->user->user[0]->conn_time); printf("disconnect user '%s' (%u) from %s with IP %s (reason: %s, time: %s)\n", rep2->user->user[0]->username, rep2->user->user[0]->id, rep2->user->user[0]->ip, get_ip(rep2->user->user[0]->local_ip, rep2->user->user[0]->local_ip6), rep2->discon_reason_txt?rep2->discon_reason_txt:"unknown", tmpbuf); } } top_update_rep__free_unpacked(rep2, &pa); rep2 = NULL; } tcsetattr(STDIN_FILENO, TCSANOW, &tio_old); ocsignal(SIGINT, old_sighandler); goto cleanup; error: fprintf(stderr, ERR_SERVER_UNREACHABLE); ret = 1; cleanup: talloc_free(data); if (rep1 != NULL) user_list_rep__free_unpacked(rep1, &pa); if (rep2 != NULL) top_update_rep__free_unpacked(rep2, &pa); free_reply(&raw); return ret; } int handle_show_id_cmd(struct unix_ctx *ctx, const char *arg, cmd_params_st *params) { int ret; struct cmd_reply_st raw; UserListRep *rep = NULL; unsigned id; IdReq req = ID_REQ__INIT; PROTOBUF_ALLOCATOR(pa, ctx); if (arg != NULL) id = atoi(arg); if (arg == NULL || need_help(arg) || id == 0) { check_cmd_help(rl_line_buffer); return 1; } init_reply(&raw); req.id = id; ret = send_cmd(ctx, CTL_CMD_ID_INFO, &req, (pack_size_func)id_req__get_packed_size, (pack_func)id_req__pack, &raw); if (ret < 0) { goto error; } rep = user_list_rep__unpack(&pa, raw.data_size, raw.data); if (rep == NULL) goto error; ret = common_info_cmd(rep, NULL, params); if (ret < 0) goto error; goto cleanup; error: fprintf(stderr, ERR_SERVER_UNREACHABLE); ret = 1; cleanup: if (rep != NULL) user_list_rep__free_unpacked(rep, &pa); free_reply(&raw); return ret; } int conn_prehandle(struct unix_ctx *ctx) { ctx->fd = connect_to_ocserv(ctx->socket_file); if (ctx->fd != -1) ctx->is_open = 1; return ctx->fd; } void conn_posthandle(struct unix_ctx *ctx) { if (ctx->is_open) { close(ctx->fd); ctx->is_open = 0; } } struct unix_ctx *conn_init(void *pool, const char *file) { struct unix_ctx *ctx; ctx = talloc_zero(pool, struct unix_ctx); if (ctx == NULL) return NULL; ctx->socket_file = file; return ctx; } void conn_close(struct unix_ctx* conn) { talloc_free(conn); }