static void check_other_work(sec_mod_st *sec) { if (need_exit) { unsigned i; for (i = 0; i < sec->key_size; i++) { gnutls_privkey_deinit(sec->key[i]); } sec_mod_client_db_deinit(sec); tls_cache_deinit(&sec->tls_db); talloc_free(sec); exit(0); } if (need_reload) { seclog(sec, LOG_DEBUG, "reloading configuration"); reload_cfg_file(sec, sec->perm_config, 0); sec->config = sec->perm_config->config; load_keys(sec, 0); need_reload = 0; } if (need_maintainance) { seclog(sec, LOG_DEBUG, "performing maintenance"); cleanup_client_entries(sec); expire_tls_sessions(sec); seclog(sec, LOG_DEBUG, "active sessions %d", sec_mod_client_db_elems(sec)); alarm(MAINTAINANCE_TIME); need_maintainance = 0; } }
static int send_sec_auth_reply(int cfd, sec_mod_st * sec, client_entry_st * entry, AUTHREP r) { SecAuthReplyMsg msg = SEC_AUTH_REPLY_MSG__INIT; int ret; if (r == AUTH__REP__OK) { /* fill message */ ret = generate_cookie(sec, entry); if (ret < 0) { seclog(sec, LOG_INFO, "cannot generate cookie"); return ret; } msg.reply = AUTH__REP__OK; msg.has_cookie = 1; msg.cookie.data = entry->cookie; msg.cookie.len = entry->cookie_size; msg.user_name = entry->auth_info.username; if (entry->msg_str != NULL) { msg.msg = entry->msg_str; } msg.has_sid = 1; msg.sid.data = entry->sid; msg.sid.len = sizeof(entry->sid); msg.has_dtls_session_id = 1; msg.dtls_session_id.data = entry->dtls_session_id; msg.dtls_session_id.len = sizeof(entry->dtls_session_id); ret = send_msg(entry, cfd, SM_CMD_AUTH_REP, &msg, (pack_size_func) sec_auth_reply_msg__get_packed_size, (pack_func) sec_auth_reply_msg__pack); } else { msg.reply = AUTH__REP__FAILED; ret = send_msg(entry, cfd, SM_CMD_AUTH_REP, &msg, (pack_size_func) sec_auth_reply_msg__get_packed_size, (pack_func) sec_auth_reply_msg__pack); } if (ret < 0) { int e = errno; seclog(sec, LOG_ERR, "send_msg: %s", strerror(e)); return ret; } talloc_free(entry->msg_str); entry->msg_str = NULL; return 0; }
static int check_user_group_status(sec_mod_st * sec, client_entry_st * e, int tls_auth_ok, const char *cert_user, char **cert_groups, unsigned cert_groups_size) { unsigned found, i; if (e->auth_type & AUTH_TYPE_CERTIFICATE) { if (tls_auth_ok == 0) { seclog(sec, LOG_INFO, "user %s "SESSION_STR" presented no certificate", e->auth_info.username, e->auth_info.psid); return -1; } e->tls_auth_ok = tls_auth_ok; if (tls_auth_ok != 0) { if (e->auth_info.username[0] == 0 && sec->config->cert_user_oid != NULL) { if (cert_user == NULL) { seclog(sec, LOG_INFO, "no username in the certificate!"); return -1; } strlcpy(e->auth_info.username, cert_user, sizeof(e->auth_info.username)); if (cert_groups_size > 0 && sec->config->cert_group_oid != NULL && e->auth_info.groupname[0] == 0) strlcpy(e->auth_info.groupname, cert_groups[0], sizeof(e->auth_info.groupname)); } else { if (sec->config->cert_user_oid != NULL && cert_user && strcmp(e->auth_info.username, cert_user) != 0) { seclog(sec, LOG_INFO, "user '%s' "SESSION_STR" presented a certificate from user '%s'", e->auth_info.username, e->auth_info.psid, cert_user); return -1; } if (sec->config->cert_group_oid != NULL) { found = 0; for (i=0;i<cert_groups_size;i++) { if (strcmp(e->auth_info.groupname, cert_groups[i]) == 0) { found++; break; } } if (found == 0) { seclog(sec, LOG_INFO, "user '%s' "SESSION_STR" presented a certificate from group '%s' but he isn't a member of it", e->auth_info.username, e->auth_info.psid, e->auth_info.groupname); return -1; } } } } } return 0; }
int handle_sec_auth_stats_cmd(sec_mod_st * sec, const CliStatsMsg * req) { client_entry_st *e; stats_st totals; if (req->sid.len != SID_SIZE) { seclog(sec, LOG_ERR, "auth session stats but with illegal sid size (%d)!", (int)req->sid.len); return -1; } e = find_client_entry(sec, req->sid.data); if (e == NULL) { char tmp[BASE64_LENGTH(SID_SIZE) + 1]; base64_encode((char *)req->sid.data, req->sid.len, (char *)tmp, sizeof(tmp)); seclog(sec, LOG_INFO, "session stats but with non-existing SID: %s", tmp); return -1; } if (e->status != PS_AUTH_COMPLETED) { seclog(sec, LOG_ERR, "session stats received in unauthenticated client %s "SESSION_STR"!", e->auth_info.username, e->auth_info.psid); return -1; } /* stats only increase */ if (req->bytes_in > e->stats.bytes_in) e->stats.bytes_in = req->bytes_in; if (req->bytes_out > e->stats.bytes_out) e->stats.bytes_out = req->bytes_out; if (req->uptime > e->stats.uptime) e->stats.uptime = req->uptime; if (req->has_discon_reason && req->discon_reason != 0) { e->discon_reason = req->discon_reason; } if (sec->perm_config->acct.amod == NULL || sec->perm_config->acct.amod->session_stats == NULL) return 0; stats_add_to(&totals, &e->stats, &e->saved_stats); if (req->remote_ip) strlcpy(e->auth_info.remote_ip, req->remote_ip, sizeof(e->auth_info.remote_ip)); if (req->ipv4) strlcpy(e->auth_info.ipv4, req->ipv4, sizeof(e->auth_info.ipv4)); if (req->ipv6) strlcpy(e->auth_info.ipv6, req->ipv6, sizeof(e->auth_info.ipv6)); sec->perm_config->acct.amod->session_stats(e->auth_type, e->auth_ctx, &e->auth_info, &totals); return 0; }
static int send_failed_session_open_reply(sec_mod_st *sec, int fd) { SecAuthSessionReplyMsg rep = SEC_AUTH_SESSION_REPLY_MSG__INIT; void *lpool; int ret; rep.reply = AUTH__REP__FAILED; lpool = talloc_new(sec); if (lpool == NULL) { return ERR_BAD_COMMAND; } ret = send_msg(lpool, fd, SM_CMD_AUTH_SESSION_REPLY, &rep, (pack_size_func) sec_auth_session_reply_msg__get_packed_size, (pack_func) sec_auth_session_reply_msg__pack); if (ret < 0) { seclog(sec, LOG_WARNING, "error in sending session reply"); ret = ERR_BAD_COMMAND; /* we desynced */ } talloc_free(lpool); return ret; }
/* returns a negative number if we have reached the score for this client. */ static void sec_mod_add_score_to_ip(sec_mod_st *sec, client_entry_st *e, const char *ip, unsigned points) { void *lpool = talloc_new(e); int ret, err; BanIpMsg msg = BAN_IP_MSG__INIT; /* no reporting if banning is disabled */ if (sec->config->max_ban_score == 0) return; msg.ip = (char*)ip; msg.score = points; msg.sid.data = e->sid; msg.sid.len = sizeof(e->sid); msg.has_sid = 1; if (lpool == NULL) { return; } ret = send_msg(lpool, sec->cmd_fd, SM_CMD_AUTH_BAN_IP, &msg, (pack_size_func) ban_ip_msg__get_packed_size, (pack_func) ban_ip_msg__pack); if (ret < 0) { err = errno; seclog(sec, LOG_WARNING, "error in sending BAN IP message: %s", strerror(err)); goto fail; } fail: talloc_free(lpool); return; }
static int send_sec_auth_reply_msg(int cfd, sec_mod_st * sec, client_entry_st * e) { SecAuthReplyMsg msg = SEC_AUTH_REPLY_MSG__INIT; int ret; msg.msg = e->msg_str; msg.passwd_counter = e->passwd_counter; if (e->passwd_counter > 0) msg.has_passwd_counter = 1; msg.reply = AUTH__REP__MSG; msg.has_sid = 1; msg.sid.data = e->sid; msg.sid.len = sizeof(e->sid); ret = send_msg(e, cfd, SM_CMD_AUTH_REP, &msg, (pack_size_func) sec_auth_reply_msg__get_packed_size, (pack_func) sec_auth_reply_msg__pack); if (ret < 0) { seclog(sec, LOG_ERR, "send_auth_reply_msg error"); } talloc_free(e->msg_str); e->msg_str = NULL; return ret; }
client_entry_st *new_client_entry(sec_mod_st *sec, const char *ip) { struct htable *db = sec->client_db; client_entry_st *e, *te; int ret; int retries = 3; e = talloc_zero(db, client_entry_st); if (e == NULL) { return NULL; } strlcpy(e->auth_info.remote_ip, ip, sizeof(e->auth_info.remote_ip)); do { ret = gnutls_rnd(GNUTLS_RND_RANDOM, e->sid, sizeof(e->sid)); if (ret < 0) { seclog(sec, LOG_ERR, "error generating SID"); goto fail; } /* check if in use */ te = find_client_entry(sec, e->sid); } while(te != NULL && retries-- >= 0); if (te != NULL) { seclog(sec, LOG_ERR, "could not generate a unique SID!"); goto fail; } base64_encode((char *)e->sid, SID_SIZE, (char *)e->auth_info.psid, sizeof(e->auth_info.psid)); e->time = time(0); if (htable_add(db, rehash(e, NULL), e) == 0) { seclog(sec, LOG_ERR, "could not add client entry to hash table"); goto fail; } return e; fail: talloc_free(e); return NULL; }
static int serve_request_worker(sec_mod_st *sec, int cfd, pid_t pid, uint8_t *buffer, unsigned buffer_size) { int ret, e; uint8_t cmd; size_t length; void *pool = buffer; /* read request */ ret = recv_msg_headers(cfd, &cmd, MAX_WAIT_SECS); if (ret < 0) { seclog(sec, LOG_DEBUG, "error receiving msg head from worker"); goto leave; } length = ret; if (length > buffer_size) { seclog(sec, LOG_INFO, "too big message (%d)", (int)length); ret = -1; goto leave; } /* read the body */ ret = force_read_timeout(cfd, buffer, length, MAX_WAIT_SECS); if (ret < 0) { e = errno; seclog(sec, LOG_INFO, "error receiving msg body: %s", strerror(e)); ret = -1; goto leave; } ret = process_worker_packet(pool, cfd, pid, sec, cmd, buffer, ret); if (ret < 0) { seclog(sec, LOG_INFO, "error processing data for '%s' command (%d)", cmd_request_to_str(cmd), ret); } leave: return ret; }
int handle_sec_auth_cont(int cfd, sec_mod_st * sec, const SecAuthContMsg * req) { client_entry_st *e; int ret; if (req->sid.len != SID_SIZE) { seclog(sec, LOG_ERR, "auth cont but with illegal sid size (%d)!", (int)req->sid.len); return -1; } e = find_client_entry(sec, req->sid.data); if (e == NULL) { seclog(sec, LOG_ERR, "auth cont but with non-existing sid!"); return -1; } if (e->status != PS_AUTH_INIT && e->status != PS_AUTH_CONT) { seclog(sec, LOG_ERR, "auth cont received for %s "SESSION_STR" but we are on state %u!", e->auth_info.username, e->auth_info.psid, e->status); ret = -1; goto cleanup; } seclog(sec, LOG_DEBUG, "auth cont for user '%s' "SESSION_STR, e->auth_info.username, e->auth_info.psid); if (req->password == NULL) { seclog(sec, LOG_ERR, "no password given in auth cont for user '%s' "SESSION_STR, e->auth_info.username, e->auth_info.psid); ret = -1; goto cleanup; } if (e->module == NULL) { seclog(sec, LOG_ERR, "no module available!"); ret = -1; goto cleanup; } e->status = PS_AUTH_CONT; ret = e->module->auth_pass(e->auth_ctx, req->password, strlen(req->password)); if (ret < 0) { if (ret != ERR_AUTH_CONTINUE) { seclog(sec, LOG_DEBUG, "error in password given in auth cont for user '%s' "SESSION_STR, e->auth_info.username, e->auth_info.psid); } goto cleanup; } cleanup: return handle_sec_auth_res(cfd, sec, e, ret); }
static void send_empty_reply(void *pool, int fd, sec_mod_st *sec) { SecmListCookiesReplyMsg msg = SECM_LIST_COOKIES_REPLY_MSG__INIT; int ret; ret = send_msg(pool, fd, CMD_SECM_LIST_COOKIES_REPLY, &msg, (pack_size_func) secm_list_cookies_reply_msg__get_packed_size, (pack_func) secm_list_cookies_reply_msg__pack); if (ret < 0) { seclog(sec, LOG_ERR, "Error sending empty show cookies reply to main"); } }
void sec_auth_user_deinit(sec_mod_st *sec, client_entry_st *e) { seclog(sec, LOG_DEBUG, "permamently closing session of user '%s' "SESSION_STR, e->auth_info.username, e->auth_info.psid); if (sec->perm_config->acct.amod != NULL && sec->perm_config->acct.amod->close_session != NULL && e->session_is_open != 0) { sec->perm_config->acct.amod->close_session(e->auth_type, e->auth_ctx, &e->auth_info, &e->saved_stats, e->discon_reason); } if (e->auth_ctx != NULL) { if (e->module) e->module->auth_deinit(e->auth_ctx); e->auth_ctx = NULL; } }
static int handle_op(void *pool, int cfd, sec_mod_st * sec, uint8_t type, uint8_t * rep, size_t rep_size) { SecOpMsg msg = SEC_OP_MSG__INIT; int ret; msg.data.data = rep; msg.data.len = rep_size; ret = send_msg(pool, cfd, type, &msg, (pack_size_func) sec_op_msg__get_packed_size, (pack_func) sec_op_msg__pack); if (ret < 0) { seclog(sec, LOG_WARNING, "sec-mod error in sending reply"); } return 0; }
static int serve_request_main(sec_mod_st *sec, int fd, uint8_t *buffer, unsigned buffer_size) { int ret, e; uint8_t cmd; size_t length; void *pool = buffer; /* read request */ ret = recv_msg_headers(fd, &cmd, MAIN_SEC_MOD_TIMEOUT); if (ret < 0) { seclog(sec, LOG_ERR, "error receiving msg head from main"); ret = ERR_BAD_COMMAND; goto leave; } length = ret; seclog(sec, LOG_DEBUG, "received request %s", cmd_request_to_str(cmd)); if (cmd <= MIN_SECM_CMD || cmd >= MAX_SECM_CMD) { seclog(sec, LOG_ERR, "received invalid message from main of %u bytes (cmd: %u)\n", (unsigned)length, (unsigned)cmd); return ERR_BAD_COMMAND; } if (length > buffer_size) { seclog(sec, LOG_ERR, "received too big message (%d)", (int)length); ret = ERR_BAD_COMMAND; goto leave; } /* read the body */ ret = force_read_timeout(fd, buffer, length, MAIN_SEC_MOD_TIMEOUT); if (ret < 0) { e = errno; seclog(sec, LOG_ERR, "error receiving msg body of cmd %u with length %u: %s", cmd, (unsigned)length, strerror(e)); ret = ERR_BAD_COMMAND; goto leave; } ret = process_packet_from_main(pool, fd, sec, cmd, buffer, ret); if (ret < 0) { seclog(sec, LOG_ERR, "error processing data for '%s' command (%d)", cmd_request_to_str(cmd), ret); } leave: return ret; }
void handle_sec_auth_ban_ip_reply(sec_mod_st *sec, const BanIpReplyMsg *msg) { client_entry_st *e; if (msg->sid.len != SID_SIZE) { seclog(sec, LOG_ERR, "ban IP reply but with illegal sid size (%d)!", (int)msg->sid.len); return; } e = find_client_entry(sec, msg->sid.data); if (e == NULL) { return; } if (msg->reply != AUTH__REP__OK) { e->status = PS_AUTH_FAILED; } return; }
static int set_module(sec_mod_st * sec, client_entry_st *e, unsigned auth_type) { unsigned i; if (auth_type == 0) return -1; /* Find the first configured authentication method which contains * the method asked by the worker, and use that. */ for (i=0;i<sec->perm_config->auth_methods;i++) { if (sec->perm_config->auth[i].enabled && (sec->perm_config->auth[i].type & auth_type) == auth_type) { e->module = sec->perm_config->auth[i].amod; e->auth_type = sec->perm_config->auth[i].type; seclog(sec, LOG_INFO, "using '%s' authentication to authenticate user "SESSION_STR, sec->perm_config->auth[i].name, e->auth_info.psid); return 0; } } return -1; }
void seclog_hex(const struct sec_mod_st* sec, int priority, const char *prefix, uint8_t* bin, unsigned bin_size, unsigned b64) { char buf[512]; int ret; size_t buf_size; gnutls_datum_t data = {bin, bin_size}; if (priority == LOG_DEBUG && sec->perm_config->debug == 0) return; if (b64) { oc_base64_encode((char*)bin, bin_size, (char*)buf, sizeof(buf)); } else { buf_size = sizeof(buf); ret = gnutls_hex_encode(&data, buf, &buf_size); if (ret < 0) return; } seclog(sec, priority, "%s %s", prefix, buf); return; }
int handle_sec_auth_init(int cfd, sec_mod_st *sec, const SecAuthInitMsg *req, pid_t pid) { int ret = -1; client_entry_st *e; unsigned need_continue = 0; e = new_client_entry(sec, req->ip, pid); if (e == NULL) { seclog(sec, LOG_ERR, "cannot initialize memory"); return -1; } ret = set_module(sec, e, req->auth_type); if (ret < 0) { seclog(sec, LOG_ERR, "no module found for auth type %u", (unsigned)req->auth_type); goto cleanup; } if (req->hostname != NULL) { strlcpy(e->hostname, req->hostname, sizeof(e->hostname)); } if (e->module) { ret = e->module->auth_init(&e->auth_ctx, e, req->user_name, req->ip, req->our_ip, pid); if (ret == ERR_AUTH_CONTINUE) { need_continue = 1; } else if (ret < 0) { goto cleanup; } ret = e->module->auth_group(e->auth_ctx, req->group_name, e->auth_info.groupname, sizeof(e->auth_info.groupname)); if (ret != 0) { ret = -1; goto cleanup; } e->auth_info.groupname[sizeof(e->auth_info.groupname) - 1] = 0; } if (req->user_name != NULL) { strlcpy(e->auth_info.username, req->user_name, sizeof(e->auth_info.username)); } if (req->our_ip != NULL) { strlcpy(e->auth_info.our_ip, req->our_ip, sizeof(e->auth_info.our_ip)); } if (e->auth_type & AUTH_TYPE_CERTIFICATE) { if (e->auth_info.groupname[0] == 0 && req->group_name != NULL && sec->config->cert_group_oid != NULL) { unsigned i, found = 0; for (i=0;i<req->n_cert_group_names;i++) { if (strcmp(req->group_name, req->cert_group_names[i]) == 0) { strlcpy(e->auth_info.groupname, req->cert_group_names[i], sizeof(e->auth_info.groupname)); found = 1; break; } } if (found == 0) { seclog(sec, LOG_AUTH, "user '%s' requested group '%s' but is not included on his certificate groups", req->user_name, req->group_name); ret = -1; goto cleanup; } } } ret = check_user_group_status(sec, e, req->tls_auth_ok, req->cert_user_name, req->cert_group_names, req->n_cert_group_names); if (ret < 0) { goto cleanup; } e->status = PS_AUTH_INIT; seclog(sec, LOG_DEBUG, "auth init %sfor user '%s' "SESSION_STR" of group: '%s' from '%s'", req->tls_auth_ok?"(with cert) ":"", e->auth_info.username, e->auth_info.psid, e->auth_info.groupname, req->ip); if (need_continue != 0) { ret = ERR_AUTH_CONTINUE; goto cleanup; } ret = 0; cleanup: return handle_sec_auth_res(cfd, sec, e, ret); }
static int process_worker_packet(void *pool, int cfd, pid_t pid, sec_mod_st * sec, cmd_request_t cmd, uint8_t * buffer, size_t buffer_size) { unsigned i; gnutls_datum_t data, out; int ret; SecOpMsg *op; PROTOBUF_ALLOCATOR(pa, pool); seclog(sec, LOG_DEBUG, "cmd [size=%d] %s\n", (int)buffer_size, cmd_request_to_str(cmd)); data.data = buffer; data.size = buffer_size; switch (cmd) { case CMD_SEC_SIGN: case CMD_SEC_DECRYPT: op = sec_op_msg__unpack(&pa, data.size, data.data); if (op == NULL) { seclog(sec, LOG_INFO, "error unpacking sec op\n"); return -1; } i = op->key_idx; if (op->has_key_idx == 0 || i >= sec->key_size) { seclog(sec, LOG_INFO, "received out-of-bounds key index (%d)", i); return -1; } data.data = op->data.data; data.size = op->data.len; if (cmd == CMD_SEC_DECRYPT) { ret = gnutls_privkey_decrypt_data(sec->key[i], 0, &data, &out); } else { #if GNUTLS_VERSION_NUMBER >= 0x030200 ret = gnutls_privkey_sign_hash(sec->key[i], 0, GNUTLS_PRIVKEY_SIGN_FLAG_TLS1_RSA, &data, &out); #else ret = gnutls_privkey_sign_raw_data(sec->key[i], 0, &data, &out); #endif } sec_op_msg__free_unpacked(op, &pa); if (ret < 0) { seclog(sec, LOG_INFO, "error in crypto operation: %s", gnutls_strerror(ret)); return -1; } ret = handle_op(pool, cfd, sec, cmd, out.data, out.size); gnutls_free(out.data); return ret; case CMD_SEC_CLI_STATS:{ CliStatsMsg *tmsg; tmsg = cli_stats_msg__unpack(&pa, data.size, data.data); if (tmsg == NULL) { seclog(sec, LOG_ERR, "error unpacking data"); return -1; } ret = handle_sec_auth_stats_cmd(sec, tmsg, pid); cli_stats_msg__free_unpacked(tmsg, &pa); return ret; } break; case CMD_SEC_AUTH_INIT:{ SecAuthInitMsg *auth_init; auth_init = sec_auth_init_msg__unpack(&pa, data.size, data.data); if (auth_init == NULL) { seclog(sec, LOG_INFO, "error unpacking auth init\n"); return -1; } ret = handle_sec_auth_init(cfd, sec, auth_init, pid); sec_auth_init_msg__free_unpacked(auth_init, &pa); return ret; } case CMD_SEC_AUTH_CONT:{ SecAuthContMsg *auth_cont; auth_cont = sec_auth_cont_msg__unpack(&pa, data.size, data.data); if (auth_cont == NULL) { seclog(sec, LOG_INFO, "error unpacking auth cont\n"); return -1; } ret = handle_sec_auth_cont(cfd, sec, auth_cont); sec_auth_cont_msg__free_unpacked(auth_cont, &pa); return ret; } case RESUME_STORE_REQ:{ SessionResumeStoreReqMsg *smsg; smsg = session_resume_store_req_msg__unpack(&pa, buffer_size, buffer); if (smsg == NULL) { seclog(sec, LOG_ERR, "error unpacking data"); return ERR_BAD_COMMAND; } ret = handle_resume_store_req(sec, smsg); /* zeroize the data */ safe_memset(buffer, 0, buffer_size); safe_memset(smsg->session_data.data, 0, smsg->session_data.len); session_resume_store_req_msg__free_unpacked(smsg, &pa); if (ret < 0) { seclog(sec, LOG_DEBUG, "could not store resumption data"); } } break; case RESUME_DELETE_REQ:{ SessionResumeFetchMsg *fmsg; fmsg = session_resume_fetch_msg__unpack(&pa, buffer_size, buffer); if (fmsg == NULL) { seclog(sec, LOG_ERR, "error unpacking data"); return ERR_BAD_COMMAND; } ret = handle_resume_delete_req(sec, fmsg); session_resume_fetch_msg__free_unpacked(fmsg, &pa); if (ret < 0) { seclog(sec, LOG_DEBUG, "could not delete resumption data."); } } break; case RESUME_FETCH_REQ:{ SessionResumeReplyMsg msg = SESSION_RESUME_REPLY_MSG__INIT; SessionResumeFetchMsg *fmsg; /* FIXME: rate limit that */ fmsg = session_resume_fetch_msg__unpack(&pa, buffer_size, buffer); if (fmsg == NULL) { seclog(sec, LOG_ERR, "error unpacking data"); return ERR_BAD_COMMAND; } ret = handle_resume_fetch_req(sec, fmsg, &msg); session_resume_fetch_msg__free_unpacked(fmsg, &pa); if (ret < 0) { msg.reply = SESSION_RESUME_REPLY_MSG__RESUME__REP__FAILED; seclog(sec, LOG_DEBUG, "could not fetch resumption data."); } else { msg.reply = SESSION_RESUME_REPLY_MSG__RESUME__REP__OK; } ret = send_msg(pool, cfd, RESUME_FETCH_REP, &msg, (pack_size_func) session_resume_reply_msg__get_packed_size, (pack_func) session_resume_reply_msg__pack); if (ret < 0) { seclog(sec, LOG_ERR, "could not send reply cmd %d.", (unsigned)cmd); return ERR_BAD_COMMAND; } } break; default: seclog(sec, LOG_WARNING, "unknown type 0x%.2x", cmd); return -1; } return 0; }
static int handle_sec_auth_session_close(sec_mod_st *sec, int fd, const SecAuthSessionMsg *req) { client_entry_st *e; int ret; CliStatsMsg rep = CLI_STATS_MSG__INIT; if (req->sid.len != SID_SIZE) { seclog(sec, LOG_ERR, "auth session close but with illegal sid size (%d)!", (int)req->sid.len); return ERR_BAD_COMMAND; } e = find_client_entry(sec, req->sid.data); if (e == NULL) { char tmp[BASE64_LENGTH(SID_SIZE) + 1]; base64_encode((char *)req->sid.data, req->sid.len, (char *)tmp, sizeof(tmp)); seclog(sec, LOG_INFO, "session close but with non-existing SID: %s", tmp); return send_msg(e, fd, SM_CMD_AUTH_CLI_STATS, &rep, (pack_size_func) cli_stats_msg__get_packed_size, (pack_func) cli_stats_msg__pack); } if (e->status < PS_AUTH_COMPLETED) { seclog(sec, LOG_DEBUG, "session close received in unauthenticated client %s "SESSION_STR"!", e->auth_info.username, e->auth_info.psid); return send_msg(e, fd, SM_CMD_AUTH_CLI_STATS, &rep, (pack_size_func) cli_stats_msg__get_packed_size, (pack_func) cli_stats_msg__pack); } if (req->has_uptime && req->uptime > e->stats.uptime) { e->stats.uptime = req->uptime; } if (req->has_bytes_in && req->bytes_in > e->stats.bytes_in) { e->stats.bytes_in = req->bytes_in; } if (req->has_bytes_out && req->bytes_out > e->stats.bytes_out) { e->stats.bytes_out = req->bytes_out; } /* send reply */ rep.bytes_in = e->stats.bytes_in; rep.bytes_out = e->stats.bytes_out; rep.has_secmod_client_entries = 1; rep.secmod_client_entries = sec_mod_client_db_elems(sec); ret = send_msg(e, fd, SM_CMD_AUTH_CLI_STATS, &rep, (pack_size_func) cli_stats_msg__get_packed_size, (pack_func) cli_stats_msg__pack); if (ret < 0) { seclog(sec, LOG_ERR, "error in sending session stats"); return ERR_BAD_COMMAND; } /* save total stats */ stats_add_to(&e->saved_stats, &e->saved_stats, &e->stats); memset(&e->stats, 0, sizeof(e->stats)); expire_client_entry(sec, e); return 0; }
static int handle_sec_auth_session_open(sec_mod_st *sec, int fd, const SecAuthSessionMsg *req) { client_entry_st *e; void *lpool; int ret; SecAuthSessionReplyMsg rep = SEC_AUTH_SESSION_REPLY_MSG__INIT; if (req->sid.len != SID_SIZE) { seclog(sec, LOG_ERR, "auth session open but with illegal sid size (%d)!", (int)req->sid.len); return send_failed_session_open_reply(sec, fd); } e = find_client_entry(sec, req->sid.data); if (e == NULL) { char tmp[BASE64_LENGTH(SID_SIZE) + 1]; base64_encode((char *)req->sid.data, req->sid.len, (char *)tmp, sizeof(tmp)); seclog(sec, LOG_INFO, "session open but with non-existing SID: %s!", tmp); return send_failed_session_open_reply(sec, fd); } if (e->status != PS_AUTH_COMPLETED) { seclog(sec, LOG_ERR, "session open received in unauthenticated client %s "SESSION_STR"!", e->auth_info.username, e->auth_info.psid); return send_failed_session_open_reply(sec, fd); } if (e->time != -1 && time(0) > e->time + sec->config->cookie_timeout) { seclog(sec, LOG_ERR, "session expired; denied session for user '%s' "SESSION_STR, e->auth_info.username, e->auth_info.psid); e->status = PS_AUTH_FAILED; return send_failed_session_open_reply(sec, fd); } if (req->has_cookie == 0 || (req->cookie.len != e->cookie_size) || memcmp(req->cookie.data, e->cookie, e->cookie_size) != 0) { seclog(sec, LOG_ERR, "cookie error; denied session for user '%s' "SESSION_STR, e->auth_info.username, e->auth_info.psid); e->status = PS_AUTH_FAILED; return send_failed_session_open_reply(sec, fd); } if (req->ipv4) strlcpy(e->auth_info.ipv4, req->ipv4, sizeof(e->auth_info.ipv4)); if (req->ipv6) strlcpy(e->auth_info.ipv6, req->ipv6, sizeof(e->auth_info.ipv6)); if (sec->perm_config->acct.amod != NULL && sec->perm_config->acct.amod->open_session != NULL && e->session_is_open == 0) { ret = sec->perm_config->acct.amod->open_session(e->auth_type, e->auth_ctx, &e->auth_info, req->sid.data, req->sid.len); if (ret < 0) { e->status = PS_AUTH_FAILED; seclog(sec, LOG_INFO, "denied session for user '%s' "SESSION_STR, e->auth_info.username, e->auth_info.psid); return send_failed_session_open_reply(sec, fd); } else { e->session_is_open = 1; } } rep.reply = AUTH__REP__OK; lpool = talloc_new(e); if (lpool == NULL) { return ERR_BAD_COMMAND; /* we desync */ } if (sec->config_module && sec->config_module->get_sup_config) { ret = sec->config_module->get_sup_config(sec->config, e, &rep, lpool); if (ret < 0) { seclog(sec, LOG_ERR, "error reading additional configuration for '%s' "SESSION_STR, e->auth_info.username, e->auth_info.psid); talloc_free(lpool); return send_failed_session_open_reply(sec, fd); } } ret = send_msg(lpool, fd, SM_CMD_AUTH_SESSION_REPLY, &rep, (pack_size_func) sec_auth_session_reply_msg__get_packed_size, (pack_func) sec_auth_session_reply_msg__pack); if (ret < 0) { seclog(sec, LOG_ERR, "error in sending session reply"); return ERR_BAD_COMMAND; /* we desync */ } talloc_free(lpool); seclog(sec, LOG_INFO, "initiating session for user '%s' "SESSION_STR, e->auth_info.username, e->auth_info.psid); e->time = -1; e->in_use++; return 0; }
static int process_packet_from_main(void *pool, int fd, sec_mod_st * sec, cmd_request_t cmd, uint8_t * buffer, size_t buffer_size) { gnutls_datum_t data; int ret; PROTOBUF_ALLOCATOR(pa, pool); seclog(sec, LOG_DEBUG, "cmd [size=%d] %s\n", (int)buffer_size, cmd_request_to_str(cmd)); data.data = buffer; data.size = buffer_size; switch (cmd) { case CMD_SECM_LIST_COOKIES: handle_secm_list_cookies_reply(pool, fd, sec); return 0; case CMD_SECM_BAN_IP_REPLY:{ BanIpReplyMsg *msg = NULL; msg = ban_ip_reply_msg__unpack(&pa, data.size, data.data); if (msg == NULL) { seclog(sec, LOG_INFO, "error unpacking auth ban ip reply\n"); return ERR_BAD_COMMAND; } handle_sec_auth_ban_ip_reply(sec, msg); ban_ip_reply_msg__free_unpacked(msg, &pa); return 0; } case CMD_SECM_SESSION_OPEN:{ SecmSessionOpenMsg *msg; msg = secm_session_open_msg__unpack(&pa, data.size, data.data); if (msg == NULL) { seclog(sec, LOG_INFO, "error unpacking session open\n"); return ERR_BAD_COMMAND; } ret = handle_secm_session_open_cmd(sec, fd, msg); secm_session_open_msg__free_unpacked(msg, &pa); return ret; } case CMD_SECM_SESSION_CLOSE:{ SecmSessionCloseMsg *msg; msg = secm_session_close_msg__unpack(&pa, data.size, data.data); if (msg == NULL) { seclog(sec, LOG_INFO, "error unpacking session close\n"); return ERR_BAD_COMMAND; } ret = handle_secm_session_close_cmd(sec, fd, msg); secm_session_close_msg__free_unpacked(msg, &pa); return ret; } default: seclog(sec, LOG_WARNING, "unknown type 0x%.2x", cmd); return ERR_BAD_COMMAND; } return 0; }
/* Performs the required steps based on the result from the * authentication function (e.g. handle_auth_init). * * @cmd: the command received * @result: the auth result */ static int handle_sec_auth_res(int cfd, sec_mod_st * sec, client_entry_st * e, int result) { int ret; passwd_msg_st pst; if ((result == ERR_AUTH_CONTINUE || result == 0) && e->module) { memset(&pst, 0, sizeof(pst)); ret = e->module->auth_msg(e->auth_ctx, e, &pst); if (ret < 0) { e->status = PS_AUTH_FAILED; seclog(sec, LOG_ERR, "error getting auth msg"); return ret; } e->msg_str = pst.msg_str; e->passwd_counter = pst.counter; } if (result == ERR_AUTH_CONTINUE) { /* if the module allows multiple retries for the password */ if (e->status != PS_AUTH_INIT && e->module && e->module->allows_retries) { sec_mod_add_score_to_ip(sec, e, e->auth_info.remote_ip, sec->config->ban_points_wrong_password); } ret = send_sec_auth_reply_msg(cfd, sec, e); if (ret < 0) { e->status = PS_AUTH_FAILED; seclog(sec, LOG_ERR, "could not send reply auth cmd."); return ret; } return 0; /* wait for another command */ } else if (result == 0 && e->status != PS_AUTH_FAILED) { /* we check status for PS_AUTH_FAILED, because status may * change async if we receive a message from main that the * user is banned */ e->status = PS_AUTH_COMPLETED; if (e->module) { e->module->auth_user(e->auth_ctx, e->auth_info.username, sizeof(e->auth_info.username)); } ret = send_sec_auth_reply(cfd, sec, e, AUTH__REP__OK); if (ret < 0) { e->status = PS_AUTH_FAILED; seclog(sec, LOG_ERR, "could not send reply auth cmd."); return ret; } ret = 0; } else { e->status = PS_AUTH_FAILED; sec_mod_add_score_to_ip(sec, e, e->auth_info.remote_ip, sec->config->ban_points_wrong_password); ret = send_sec_auth_reply(cfd, sec, e, AUTH__REP__FAILED); if (ret < 0) { seclog(sec, LOG_ERR, "could not send reply auth cmd."); return ret; } if (result < 0) { ret = result; } else { seclog(sec, LOG_ERR, "unexpected auth result: %d\n", result); ret = ERR_BAD_COMMAND; } } return ret; }
void handle_secm_list_cookies_reply(void *pool, int fd, sec_mod_st *sec) { SecmListCookiesReplyMsg msg = SECM_LIST_COOKIES_REPLY_MSG__INIT; struct htable *db = sec->client_db; client_entry_st *t; struct htable_iter iter; CookieIntMsg *cookies; int ret; time_t now = time(0); if (db == NULL) { send_empty_reply(pool, fd, sec); return; } seclog(sec, LOG_DEBUG, "sending list cookies reply to main"); msg.cookies = talloc_size(pool, sizeof(CookieIntMsg*)*db->elems); if (msg.cookies == NULL) { send_empty_reply(pool, fd, sec); return; } cookies = talloc_size(pool, sizeof(CookieIntMsg)*db->elems); if (cookies == NULL) { send_empty_reply(pool, fd, sec); return; } t = htable_first(db, &iter); while (t != NULL) { if IS_CLIENT_ENTRY_EXPIRED(sec, t, now) goto cont; if (msg.n_cookies >= db->elems) break; cookie_int_msg__init(&cookies[msg.n_cookies]); cookies[msg.n_cookies].safe_id.data = (void*)t->acct_info.safe_id; cookies[msg.n_cookies].safe_id.len = sizeof(t->acct_info.safe_id); cookies[msg.n_cookies].session_is_open = t->session_is_open; cookies[msg.n_cookies].tls_auth_ok = t->tls_auth_ok; if (t->created > 0) cookies[msg.n_cookies].created = t->created; else cookies[msg.n_cookies].created = 0; /* a session which is in use, does not expire */ if (t->exptime > 0 && t->in_use == 0) cookies[msg.n_cookies].expires = t->exptime; else cookies[msg.n_cookies].expires = 0; cookies[msg.n_cookies].username = t->acct_info.username; cookies[msg.n_cookies].groupname = t->acct_info.groupname; cookies[msg.n_cookies].user_agent = t->acct_info.user_agent; cookies[msg.n_cookies].remote_ip = t->acct_info.remote_ip; cookies[msg.n_cookies].status = t->status; cookies[msg.n_cookies].in_use = t->in_use; cookies[msg.n_cookies].vhost = VHOSTNAME(t->vhost); msg.cookies[msg.n_cookies] = &cookies[msg.n_cookies]; msg.n_cookies++; cont: t = htable_next(db, &iter); } ret = send_msg(pool, fd, CMD_SECM_LIST_COOKIES_REPLY, &msg, (pack_size_func) secm_list_cookies_reply_msg__get_packed_size, (pack_func) secm_list_cookies_reply_msg__pack); if (ret < 0) { seclog(sec, LOG_ERR, "Error sending show cookies reply to main"); } talloc_free(msg.cookies); talloc_free(cookies); }
static int load_keys(sec_mod_st *sec, unsigned force) { unsigned i, need_reload = 0; int ret; struct pin_st pins; static time_t last_access = 0; for (i = 0; i < sec->perm_config->key_size; i++) { if (need_file_reload(sec->perm_config->key[i], last_access) != 0) { need_reload = 1; break; } } if (need_reload == 0) return 0; last_access = time(0); ret = load_pins(sec->perm_config, &pins); if (ret < 0) { seclog(sec, LOG_ERR, "error loading PIN files"); exit(1); } /* Reminder: the number of private keys or their filenames cannot be changed on reload */ if (sec->key == NULL) { sec->key_size = sec->perm_config->key_size; sec->key = talloc_zero_size(sec, sizeof(*sec->key) * sec->perm_config->key_size); if (sec->key == NULL) { seclog(sec, LOG_ERR, "error in memory allocation"); exit(1); } } /* read private keys */ for (i = 0; i < sec->key_size; i++) { gnutls_privkey_t p; ret = gnutls_privkey_init(&p); CHECK_LOOP_ERR(ret); /* load the private key */ if (gnutls_url_is_supported(sec->perm_config->key[i]) != 0) { gnutls_privkey_set_pin_function(p, pin_callback, &pins); ret = gnutls_privkey_import_url(p, sec->perm_config->key[i], 0); CHECK_LOOP_ERR(ret); } else { gnutls_datum_t data; ret = gnutls_load_file(sec->perm_config->key[i], &data); if (ret < 0) { seclog(sec, LOG_ERR, "error loading file '%s'", sec->perm_config->key[i]); CHECK_LOOP_ERR(ret); } ret = gnutls_privkey_import_x509_raw(p, &data, GNUTLS_X509_FMT_PEM, NULL, 0); if (ret == GNUTLS_E_DECRYPTION_FAILED && pins.pin[0]) { ret = gnutls_privkey_import_x509_raw(p, &data, GNUTLS_X509_FMT_PEM, pins.pin, 0); } CHECK_LOOP_ERR(ret); gnutls_free(data.data); } if (sec->key[i] != NULL) { gnutls_privkey_deinit(sec->key[i]); } sec->key[i] = p; } return 0; }
/* sec_mod_server: * @config: server configuration * @socket_file: the name of the socket * @cmd_fd: socket to exchange commands with main * @cmd_fd_sync: socket to received sync commands from main * * This is the main part of the security module. * It creates the unix domain socket identified by @socket_file * and then accepts connections from the workers to it. Then * it serves commands requested on the server's private key. * * When the operation is decrypt the provided data are * decrypted and sent back to worker. The sign operation * signs the provided data. * * The security module's reply to the worker has the * following format: * byte[0-5]: length (uint32_t) * byte[5-total]: data (signature or decrypted data) * * The reason for having this as a separate process * is to avoid any bug on the workers to leak the key. * It is not part of main because workers are spawned * from main, and thus should be prevented from accessing * parts the key in stack or heap that was not zeroized. * Other than that it allows the main server to spawn * clients fast without becoming a bottleneck due to private * key operations. */ void sec_mod_server(void *main_pool, struct perm_cfg_st *perm_config, const char *socket_file, int cmd_fd, int cmd_fd_sync) { struct sockaddr_un sa; socklen_t sa_len; int cfd, ret, e, n; unsigned buffer_size; uid_t uid; uint8_t *buffer; int sd; sec_mod_st *sec; void *sec_mod_pool; fd_set rd_set; pid_t pid; #ifdef HAVE_PSELECT struct timespec ts; #else struct timeval ts; #endif sigset_t emptyset, blockset; #ifdef DEBUG_LEAKS talloc_enable_leak_report_full(); #endif sigemptyset(&blockset); sigemptyset(&emptyset); sigaddset(&blockset, SIGALRM); sigaddset(&blockset, SIGTERM); sigaddset(&blockset, SIGINT); sigaddset(&blockset, SIGHUP); sec_mod_pool = talloc_init("sec-mod"); if (sec_mod_pool == NULL) { seclog(sec, LOG_ERR, "error in memory allocation"); exit(1); } sec = talloc_zero(sec_mod_pool, sec_mod_st); if (sec == NULL) { seclog(sec, LOG_ERR, "error in memory allocation"); exit(1); } sec->perm_config = talloc_steal(sec, perm_config); sec->config = sec->perm_config->config; tls_cache_init(sec, &sec->tls_db); sup_config_init(sec); memset(&sa, 0, sizeof(sa)); sa.sun_family = AF_UNIX; strlcpy(sa.sun_path, socket_file, sizeof(sa.sun_path)); remove(socket_file); #define SOCKET_FILE sa.sun_path /* we no longer need the main pool after this point. */ talloc_free(main_pool); ocsignal(SIGHUP, handle_sighup); ocsignal(SIGINT, handle_sigterm); ocsignal(SIGTERM, handle_sigterm); ocsignal(SIGALRM, handle_alarm); sec_auth_init(sec, perm_config); sec->cmd_fd = cmd_fd; sec->cmd_fd_sync = cmd_fd_sync; #ifdef HAVE_PKCS11 ret = gnutls_pkcs11_reinit(); if (ret < 0) { seclog(sec, LOG_WARNING, "error in PKCS #11 reinitialization: %s", gnutls_strerror(ret)); } #endif if (sec_mod_client_db_init(sec) == NULL) { seclog(sec, LOG_ERR, "error in client db initialization"); exit(1); } sd = socket(AF_UNIX, SOCK_STREAM, 0); if (sd == -1) { e = errno; seclog(sec, LOG_ERR, "could not create socket '%s': %s", SOCKET_FILE, strerror(e)); exit(1); } set_cloexec_flag(sd, 1); umask(066); ret = bind(sd, (struct sockaddr *)&sa, SUN_LEN(&sa)); if (ret == -1) { e = errno; seclog(sec, LOG_ERR, "could not bind socket '%s': %s", SOCKET_FILE, strerror(e)); exit(1); } ret = chown(SOCKET_FILE, perm_config->uid, perm_config->gid); if (ret == -1) { e = errno; seclog(sec, LOG_INFO, "could not chown socket '%s': %s", SOCKET_FILE, strerror(e)); } ret = listen(sd, 1024); if (ret == -1) { e = errno; seclog(sec, LOG_ERR, "could not listen to socket '%s': %s", SOCKET_FILE, strerror(e)); exit(1); } ret = load_keys(sec, 1); if (ret < 0) { seclog(sec, LOG_ERR, "error loading private key files"); exit(1); } sigprocmask(SIG_BLOCK, &blockset, &sig_default_set); alarm(MAINTAINANCE_TIME); seclog(sec, LOG_INFO, "sec-mod initialized (socket: %s)", SOCKET_FILE); for (;;) { check_other_work(sec); FD_ZERO(&rd_set); n = 0; FD_SET(cmd_fd, &rd_set); n = MAX(n, cmd_fd); FD_SET(cmd_fd_sync, &rd_set); n = MAX(n, cmd_fd_sync); FD_SET(sd, &rd_set); n = MAX(n, sd); #ifdef HAVE_PSELECT ts.tv_nsec = 0; ts.tv_sec = 120; ret = pselect(n + 1, &rd_set, NULL, NULL, &ts, &emptyset); #else ts.tv_usec = 0; ts.tv_sec = 120; sigprocmask(SIG_UNBLOCK, &blockset, NULL); ret = select(n + 1, &rd_set, NULL, NULL, &ts); sigprocmask(SIG_BLOCK, &blockset, NULL); #endif if (ret == 0 || (ret == -1 && errno == EINTR)) continue; if (ret < 0) { e = errno; seclog(sec, LOG_ERR, "Error in pselect(): %s", strerror(e)); exit(1); } /* we do a new allocation, to also use it as pool for the * parsers to use */ buffer_size = MAX_MSG_SIZE; buffer = talloc_size(sec, buffer_size); if (buffer == NULL) { seclog(sec, LOG_ERR, "error in memory allocation"); exit(1); } /* we use two fds for communication with main. The synchronous is for * ping-pong communication which each request is answered immediated. The * async is for messages sent back and forth in no particular order */ if (FD_ISSET(cmd_fd_sync, &rd_set)) { ret = serve_request_main(sec, cmd_fd_sync, buffer, buffer_size); if (ret < 0 && ret == ERR_BAD_COMMAND) { seclog(sec, LOG_ERR, "error processing sync command from main"); exit(1); } } if (FD_ISSET(cmd_fd, &rd_set)) { ret = serve_request_main(sec, cmd_fd, buffer, buffer_size); if (ret < 0 && ret == ERR_BAD_COMMAND) { seclog(sec, LOG_ERR, "error processing async command from main"); exit(1); } } if (FD_ISSET(sd, &rd_set)) { sa_len = sizeof(sa); cfd = accept(sd, (struct sockaddr *)&sa, &sa_len); if (cfd == -1) { e = errno; if (e != EINTR) { seclog(sec, LOG_DEBUG, "sec-mod error accepting connection: %s", strerror(e)); goto cont; } } set_cloexec_flag (cfd, 1); /* do not allow unauthorized processes to issue commands */ ret = check_upeer_id("sec-mod", sec->perm_config->debug, cfd, perm_config->uid, perm_config->gid, &uid, &pid); if (ret < 0) { seclog(sec, LOG_INFO, "rejected unauthorized connection"); } else { memset(buffer, 0, buffer_size); serve_request_worker(sec, cfd, pid, buffer, buffer_size); } close(cfd); } cont: talloc_free(buffer); #ifdef DEBUG_LEAKS talloc_report_full(sec, stderr); #endif } }