/* * Copyright (C) 2013, 2014 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 "html.h" #include #include #include #include #include #define VERSION_MSG "0.1(1)\n" static const char oc_success_msg_head[] = "\n" "\n" VERSION_MSG "\n" "SSL VPN Service"; static const char oc_success_msg_foot[] = "\n"; static const char ocv3_success_msg_head[] = "\n" "\n" "SSL VPN Service"; static const char ocv3_success_msg_foot[] = "\n"; #define OC_LOGIN_MSG_START \ "\n" \ "\n" \ VERSION_MSG \ "\n" \ "%s\n" \ "
\n" static const char oc_login_msg_end[] = "
\n" "
"; static const char login_msg_user[] = "\n"; static const char login_msg_password[] = "\n"; #define OCV3_LOGIN_MSG_START \ "\n" \ "\n" \ "%s\n" \ "
\n" static const char ocv3_login_msg_end[] = "
\n"; static int get_cert_info(worker_st * ws); static int append_group_idx(worker_st * ws, str_st *str, unsigned i) { char temp[128]; const char *name; const char *value; value = ws->config->group_list[i]; if (ws->config->friendly_group_list != NULL && ws->config->friendly_group_list[i] != NULL) name = ws->config->friendly_group_list[i]; else name = ws->config->group_list[i]; snprintf(temp, sizeof(temp), "\n", value, name); if (str_append_str(str, temp) < 0) return -1; return 0; } static int append_group_str(worker_st * ws, str_st *str, const char *group) { char temp[128]; const char *name; const char *value; unsigned i; value = name = group; if (ws->config->friendly_group_list) { for (i=0;iconfig->group_list_size;i++) { if (strcmp(ws->config->group_list[i], group) == 0) { if (ws->config->friendly_group_list[i] != NULL) name = ws->config->friendly_group_list[i]; break; } } } snprintf(temp, sizeof(temp), "\n", value, name); if (str_append_str(str, temp) < 0) return -1; return 0; } int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg) { int ret; char context[BASE64_LENGTH(SID_SIZE) + 1]; unsigned int i, j; str_st str; const char *login_msg_start; const char *login_msg_end; if (ws->req.user_agent_type == AGENT_OPENCONNECT_V3) { login_msg_start = OCV3_LOGIN_MSG_START; login_msg_end = ocv3_login_msg_end; } else { login_msg_start = OC_LOGIN_MSG_START; login_msg_end = oc_login_msg_end; } str_init(&str, ws); cstp_cork(ws); ret = cstp_printf(ws, "HTTP/1.%u 200 OK\r\n", http_ver); if (ret < 0) return -1; ret = cstp_puts(ws, "Connection: Keep-Alive\r\n"); if (ret < 0) return -1; if (ws->sid_set != 0) { base64_encode((char *)ws->sid, sizeof(ws->sid), (char *)context, sizeof(context)); ret = cstp_printf(ws, "Set-Cookie: webvpncontext=%s; Max-Age=%u; Secure\r\n", context, (unsigned)MAX_AUTH_SECS); if (ret < 0) return -1; oclog(ws, LOG_DEBUG, "sent sid: %s", context); } ret = cstp_puts(ws, "Content-Type: text/xml\r\n"); if (ret < 0) { ret = -1; goto cleanup; } if (ws->auth_state == S_AUTH_REQ) { /* only ask password */ if (pmsg == NULL) pmsg = "Please enter your password"; ret = str_append_printf(&str, login_msg_start, pmsg); if (ret < 0) { ret = -1; goto cleanup; } ret = str_append_str(&str, login_msg_password); if (ret < 0) { ret = -1; goto cleanup; } ret = str_append_str(&str, login_msg_end); if (ret < 0) { ret = -1; goto cleanup; } } else { if (pmsg == NULL) pmsg = "Please enter your username"; /* ask for username and groups */ ret = str_append_printf(&str, login_msg_start, pmsg); if (ret < 0) { ret = -1; goto cleanup; } if (ws->config->auth_types & AUTH_TYPE_USERNAME_PASS) { ret = str_append_str(&str, login_msg_user); if (ret < 0) { ret = -1; goto cleanup; } } if (ws->config->auth_types & AUTH_TYPE_CERTIFICATE && ws->cert_auth_ok != 0) { ret = get_cert_info(ws); if (ret < 0) { ret = -1; oclog(ws, LOG_WARNING, "cannot obtain certificate information"); goto cleanup; } } /* send groups */ if (ws->config->group_list_size > 0 || ws->cert_groups_size > 0) { ret = str_append_str(&str, "\n"); if (ret < 0) { ret = -1; goto cleanup; } } ret = str_append_str(&str, login_msg_end); if (ret < 0) { ret = -1; goto cleanup; } } ret = cstp_printf(ws, "Content-Length: %u\r\n", (unsigned int)str.length); if (ret < 0) { ret = -1; goto cleanup; } ret = cstp_puts(ws, "X-Transcend-Version: 1\r\n"); if (ret < 0) { ret = -1; goto cleanup; } ret = cstp_puts(ws, "\r\n"); if (ret < 0) { ret = -1; goto cleanup; } ret = cstp_send(ws, str.data, str.length); if (ret < 0) { ret = -1; goto cleanup; } ret = cstp_uncork(ws); if (ret < 0) { ret = -1; goto cleanup; } ret = 0; cleanup: str_clear(&str); return ret; } int get_auth_handler(worker_st * ws, unsigned http_ver) { return get_auth_handler2(ws, http_ver, NULL); } int get_cert_names(worker_st * ws, const gnutls_datum_t * raw) { gnutls_x509_crt_t crt; int ret; unsigned i; size_t size; if (ws->cert_username[0] != 0 || ws->cert_groups_size > 0) return 0; /* already read, nothing to do */ ret = gnutls_x509_crt_init(&crt); if (ret < 0) { oclog(ws, LOG_ERR, "certificate init error: %s", gnutls_strerror(ret)); goto fail; } ret = gnutls_x509_crt_import(crt, raw, GNUTLS_X509_FMT_DER); if (ret < 0) { oclog(ws, LOG_ERR, "certificate import error: %s", gnutls_strerror(ret)); goto fail; } size = sizeof(ws->cert_username); if (ws->config->cert_user_oid) { /* otherwise certificate username is ignored */ ret = gnutls_x509_crt_get_dn_by_oid(crt, ws->config->cert_user_oid, 0, 0, ws->cert_username, &size); } else { ret = gnutls_x509_crt_get_dn(crt, ws->cert_username, &size); } if (ret < 0) { if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) oclog(ws, LOG_ERR, "certificate's username exceed the maximum buffer size (%u)", (unsigned)sizeof(ws->cert_username)); else oclog(ws, LOG_ERR, "cannot obtain user from certificate DN: %s", gnutls_strerror(ret)); goto fail; } if (ws->config->cert_group_oid) { i = 0; do { ws->cert_groups = talloc_realloc(ws, ws->cert_groups, char*, i+1); if (ws->cert_groups == NULL) { oclog(ws, LOG_ERR, "cannot allocate memory for cert groups"); ret = -1; goto fail; } size = 0; ret = gnutls_x509_crt_get_dn_by_oid(crt, ws->config->cert_group_oid, i, 0, NULL, &size); if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) break; if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) { if (ret == 0) ret = GNUTLS_E_INTERNAL_ERROR; oclog(ws, LOG_ERR, "cannot obtain group from certificate DN: %s", gnutls_strerror(ret)); goto fail; } ws->cert_groups[i] = talloc_size(ws->cert_groups, size); if (ws->cert_groups[i] == NULL) { oclog(ws, LOG_ERR, "cannot allocate memory for cert group"); ret = -1; goto fail; } ret = gnutls_x509_crt_get_dn_by_oid(crt, ws->config->cert_group_oid, i, 0, ws->cert_groups[i], &size); if (ret < 0) { oclog(ws, LOG_ERR, "cannot obtain group from certificate DN: %s", gnutls_strerror(ret)); goto fail; } i++; } while (ret >= 0); ws->cert_groups_size = i; } ret = 0; fail: gnutls_x509_crt_deinit(crt); return ret; } /* auth reply from main process */ static int recv_cookie_auth_reply(worker_st * ws) { unsigned i; int ret; int socketfd = -1; AuthReplyMsg *msg = NULL; PROTOBUF_ALLOCATOR(pa, ws); ret = recv_socket_msg(ws, ws->cmd_fd, AUTH_COOKIE_REP, &socketfd, (void *)&msg, (unpack_func) auth_reply_msg__unpack); if (ret < 0) { oclog(ws, LOG_ERR, "error receiving auth reply message"); return ret; } oclog(ws, LOG_DEBUG, "received auth reply message (value: %u)", (unsigned)msg->reply); switch (msg->reply) { case AUTH__REP__OK: if (socketfd != -1) { ws->tun_fd = socketfd; if (msg->vname == NULL || msg->user_name == NULL) { ret = ERR_AUTH_FAIL; goto cleanup; } strlcpy(ws->vinfo.name, msg->vname, sizeof(ws->vinfo.name)); strlcpy(ws->username, msg->user_name, sizeof(ws->username)); if (msg->group_name != NULL) { strlcpy(ws->groupname, msg->group_name, sizeof(ws->groupname)); } else { ws->groupname[0] = 0; } memcpy(ws->session_id, msg->session_id.data, msg->session_id.len); if (msg->ipv4 != NULL) { talloc_free(ws->vinfo.ipv4); if (strcmp(msg->ipv4, "0.0.0.0") == 0) ws->vinfo.ipv4 = NULL; else ws->vinfo.ipv4 = talloc_strdup(ws, msg->ipv4); } if (msg->ipv6 != NULL) { talloc_free(ws->vinfo.ipv6); if (strcmp(msg->ipv6, "::") == 0) ws->vinfo.ipv6 = NULL; else ws->vinfo.ipv6 = talloc_strdup(ws, msg->ipv6); } if (msg->ipv4_local != NULL) { talloc_free(ws->vinfo.ipv4_local); if (strcmp(msg->ipv4_local, "0.0.0.0") == 0) ws->vinfo.ipv4_local = NULL; else ws->vinfo.ipv4_local = talloc_strdup(ws, msg->ipv4_local); } if (msg->ipv6_local != NULL) { talloc_free(ws->vinfo.ipv6_local); if (strcmp(msg->ipv6_local, "::") == 0) ws->vinfo.ipv6_local = NULL; else ws->vinfo.ipv6_local = talloc_strdup(ws, msg->ipv6_local); } /* Read any additional data */ if (msg->ipv4_netmask != NULL) { talloc_free(ws->config->network.ipv4_netmask); ws->config->network.ipv4_netmask = talloc_strdup(ws, msg->ipv4_netmask); } if (msg->ipv4_network != NULL) { talloc_free(ws->config->network.ipv4_network); ws->config->network.ipv4_network = talloc_strdup(ws, msg->ipv4_network); } if (msg->ipv6_network != NULL) { talloc_free(ws->config->network.ipv6_network); ws->config->network.ipv6_network = talloc_strdup(ws, msg->ipv6_network); } if (msg->has_ipv6_prefix) { ws->config->network.ipv6_prefix = msg->ipv6_prefix; } if (msg->has_rx_per_sec) ws->config->rx_per_sec = msg->rx_per_sec; if (msg->has_tx_per_sec) ws->config->tx_per_sec = msg->tx_per_sec; if (msg->has_net_priority) ws->config->net_priority = msg->net_priority; if (msg->has_no_udp && msg->no_udp != 0) ws->config->udp_port = 0; if (msg->xml_config_file) { talloc_free(ws->config->xml_config_file); ws->config->xml_config_file = talloc_strdup(ws, msg->xml_config_file); } /* routes */ ws->routes = talloc_size(ws, msg->n_routes*sizeof(char*)); if (ws->routes != NULL) { ws->routes_size = msg->n_routes; for (i = 0; i < ws->routes_size; i++) { ws->routes[i] = talloc_strdup(ws, msg->routes[i]); /* If a default route is detected */ if (ws->routes[i] != NULL && (strcmp(ws->routes[i], "default") == 0 || strcmp(ws->routes[i], "0.0.0.0/0") == 0)) { /* disable all routes */ ws->routes_size = 0; ws->default_route = 1; break; } } } if (check_if_default_route(ws->routes, ws->routes_size)) ws->default_route = 1; ws->dns = talloc_size(ws, msg->n_dns*sizeof(char*)); if (ws->dns != NULL) { ws->dns_size = msg->n_dns; for (i = 0; i < ws->dns_size; i++) { ws->dns[i] = talloc_strdup(ws, msg->dns[i]); } } ws->nbns = talloc_size(ws, msg->n_nbns*sizeof(char*)); if (ws->nbns != NULL) { ws->nbns_size = msg->n_nbns; for (i = 0; i < ws->nbns_size; i++) { ws->nbns[i] = talloc_strdup(ws, msg->nbns[i]); } } } else { oclog(ws, LOG_ERR, "error in received message"); ret = ERR_AUTH_FAIL; goto cleanup; } break; case AUTH__REP__FAILED: default: if (msg->reply != AUTH__REP__FAILED) oclog(ws, LOG_ERR, "unexpected auth reply %u", (unsigned)msg->reply); ret = ERR_AUTH_FAIL; goto cleanup; } ret = 0; cleanup: if (msg != NULL) auth_reply_msg__free_unpacked(msg, &pa); return ret; } /* returns the fd */ int connect_to_secmod(worker_st * ws) { int sd, ret, e; sd = socket(AF_UNIX, SOCK_STREAM, 0); if (sd == -1) { e = errno; oclog(ws, LOG_ERR, "error opening unix socket (for sec-mod) %s", strerror(e)); return -1; } ret = connect(sd, (struct sockaddr *)&ws->secmod_addr, ws->secmod_addr_len); if (ret < 0) { e = errno; close(sd); oclog(ws, LOG_ERR, "error connecting to sec-mod socket '%s': %s", ws->secmod_addr.sun_path, strerror(e)); return -1; } return sd; } static int recv_auth_reply(worker_st * ws, int sd, char *txt, size_t max_txt_size) { int ret; SecAuthReplyMsg *msg = NULL; PROTOBUF_ALLOCATOR(pa, ws); ret = recv_msg(ws, sd, SM_CMD_AUTH_REP, (void *)&msg, (unpack_func) sec_auth_reply_msg__unpack); if (ret < 0) { oclog(ws, LOG_ERR, "error receiving auth reply message"); return ret; } oclog(ws, LOG_DEBUG, "received auth reply message (value: %u)", (unsigned)msg->reply); switch (msg->reply) { case AUTH__REP__MSG: if (txt == NULL || msg->msg == NULL) { oclog(ws, LOG_ERR, "received unexpected msg"); return ERR_AUTH_FAIL; } strlcpy(txt, msg->msg, max_txt_size); if (msg->has_sid && msg->sid.len == sizeof(ws->sid)) { /* update our sid */ memcpy(ws->sid, msg->sid.data, sizeof(ws->sid)); ws->sid_set = 1; } ret = ERR_AUTH_CONTINUE; goto cleanup; case AUTH__REP__OK: if (msg->user_name == NULL) { ret = ERR_AUTH_FAIL; goto cleanup; } strlcpy(ws->username, msg->user_name, sizeof(ws->username)); if (msg->has_sid && msg->sid.len == sizeof(ws->sid)) { /* update our sid */ memcpy(ws->sid, msg->sid.data, sizeof(ws->sid)); ws->sid_set = 1; } if (msg->has_cookie == 0 || msg->cookie.len == 0 || msg->dtls_session_id.len != sizeof(ws->session_id)) { ret = ERR_AUTH_FAIL; goto cleanup; } ws->cookie = talloc_memdup(ws, msg->cookie.data, msg->cookie.len); if (ws->cookie) { ws->cookie_size = msg->cookie.len; ws->cookie_set = 1; } memcpy(ws->session_id, msg->dtls_session_id.data, msg->dtls_session_id.len); break; case AUTH__REP__FAILED: default: if (msg->reply != AUTH__REP__FAILED) oclog(ws, LOG_ERR, "unexpected auth reply %u", (unsigned)msg->reply); ret = ERR_AUTH_FAIL; goto cleanup; } ret = 0; cleanup: if (msg != NULL) sec_auth_reply_msg__free_unpacked(msg, &pa); return ret; } /* grabs the username from the session certificate */ static int get_cert_info(worker_st * ws) { const gnutls_datum_t *cert; unsigned int ncerts; int ret; /* this is superflous. Verification has already been performed * during handshake. */ cert = gnutls_certificate_get_peers(ws->session, &ncerts); if (cert == NULL) { return -1; } ret = get_cert_names(ws, cert); if (ret < 0) { if (ws->config->cert_user_oid == NULL) { oclog(ws, LOG_ERR, "cannot read username from certificate; no cert-user-oid is set"); } else { oclog(ws, LOG_ERR, "cannot read username (%s) from certificate", ws->config->cert_user_oid); } return -1; } return 0; } /* sends a cookie authentication request to main thread and waits for * a reply. * Returns 0 on success. */ int auth_cookie(worker_st * ws, void *cookie, size_t cookie_size) { int ret; AuthCookieRequestMsg msg = AUTH_COOKIE_REQUEST_MSG__INIT; if ((ws->config->auth_types & AUTH_TYPE_CERTIFICATE) && ws->config->cisco_client_compat == 0) { if (((ws->config->auth_types & AUTH_TYPE_CERTIFICATE_OPT) != AUTH_TYPE_CERTIFICATE_OPT && ws->cert_auth_ok == 0)) { oclog(ws, LOG_INFO, "no certificate provided for cookie authentication"); return -1; } if (ws->cert_auth_ok != 0) { ret = get_cert_info(ws); if (ret < 0) { oclog(ws, LOG_INFO, "cannot obtain certificate info"); return -1; } } } msg.cookie.data = cookie; msg.cookie.len = cookie_size; ret = send_msg_to_main(ws, AUTH_COOKIE_REQ, &msg, (pack_size_func) auth_cookie_request_msg__get_packed_size, (pack_func) auth_cookie_request_msg__pack); if (ret < 0) { oclog(ws, LOG_INFO, "error sending cookie authentication request"); return ret; } ret = recv_cookie_auth_reply(ws); if (ret < 0) { oclog(ws, LOG_INFO, "error receiving cookie authentication reply"); return ret; } return 0; } int post_common_handler(worker_st * ws, unsigned http_ver) { int ret, size; char str_cookie[BASE64_LENGTH(ws->cookie_size)+1]; size_t str_cookie_size = sizeof(str_cookie); char msg[MAX_BANNER_SIZE + 32]; const char *success_msg_head; const char *success_msg_foot; unsigned success_msg_head_size; unsigned success_msg_foot_size; if (ws->req.user_agent_type == AGENT_OPENCONNECT_V3) { success_msg_head = ocv3_success_msg_head; success_msg_foot = ocv3_success_msg_foot; success_msg_head_size = sizeof(ocv3_success_msg_head)-1; success_msg_foot_size = sizeof(ocv3_success_msg_foot)-1; } else { success_msg_head = oc_success_msg_head; success_msg_foot = oc_success_msg_foot; success_msg_head_size = sizeof(oc_success_msg_head)-1; success_msg_foot_size = sizeof(oc_success_msg_foot)-1; } base64_encode((char *)ws->cookie, ws->cookie_size, (char *)str_cookie, str_cookie_size); /* reply */ cstp_cork(ws); ret = cstp_printf(ws, "HTTP/1.%u 200 OK\r\n", http_ver); if (ret < 0) return -1; ret = cstp_puts(ws, "Connection: Keep-Alive\r\n"); if (ret < 0) return -1; ret = cstp_puts(ws, "Content-Type: text/xml\r\n"); if (ret < 0) return -1; if (ws->config->banner) { size = snprintf(msg, sizeof(msg), "%s", ws->config->banner); if (size <= 0) return -1; /* snprintf() returns not a very useful value, so we need to recalculate */ size = strlen(msg); } else { msg[0] = 0; size = 0; } size += success_msg_head_size + success_msg_foot_size; ret = cstp_printf(ws, "Content-Length: %u\r\n", (unsigned)size); if (ret < 0) return -1; ret = cstp_puts(ws, "X-Transcend-Version: 1\r\n"); if (ret < 0) return -1; ret = cstp_printf(ws, "Set-Cookie: webvpn=%s; Secure\r\n", str_cookie); if (ret < 0) return -1; #ifdef ANYCONNECT_CLIENT_COMPAT ret = cstp_puts(ws, "Set-Cookie: webvpnc=; expires=Thu, 01 Jan 1970 22:00:00 GMT; path=/; Secure\r\n"); if (ret < 0) return -1; if (ws->config->xml_config_file) { ret = cstp_printf(ws, "Set-Cookie: webvpnc=bu:/&p:t&iu:1/&sh:%s&lu:/+CSCOT+/translation-table?textdomain%%3DAnyConnect%%26type%%3Dmanifest&fu:profiles%%2F%s&fh:%s; path=/; Secure\r\n", ws->config->cert_hash, ws->config->xml_config_file, ws->config->xml_config_hash); } else { ret = cstp_printf(ws, "Set-Cookie: webvpnc=bu:/&p:t&iu:1/&sh:%s; path=/; Secure\r\n", ws->config->cert_hash); } if (ret < 0) return -1; #endif ret = cstp_printf(ws, "\r\n%s%s%s", success_msg_head, msg, success_msg_foot); if (ret < 0) return -1; ret = cstp_uncork(ws); if (ret < 0) return -1; return 0; } #define XMLUSER "" #define XMLPASS "" #define XMLUSER_END "" #define XMLPASS_END "" /* Returns the contents of the provided fields in a newly allocated * string, or a negative value on error. * * @body: is the string to search the xml field at, should be null-terminated. * @xml_field: the XML field to check for (e.g., MYFIELD) * @value: the value that was found */ static int parse_reply(worker_st * ws, char *body, unsigned body_length, const char *field, unsigned field_size, const char *xml_field, unsigned xml_field_size, char **value) { char *p; char temp1[64]; char temp2[64]; unsigned temp2_len, temp1_len; unsigned len, xml = 0; if (memmem(body, body_length, "", field); snprintf(temp2, sizeof(temp2), "", field); temp1_len = strlen(temp1); temp2_len = strlen(temp2); /* body should contain test */ *value = strcasestr(body, temp1); if (*value == NULL) { oclog(ws, LOG_DEBUG, "cannot find '%s' in client XML message", field); return -1; } *value += temp1_len; p = *value; len = 0; while (*p != 0) { if (*p == '<' && (strncasecmp(p, temp2, temp2_len) == 0)) { break; } p++; len++; } } else { /* non-xml version */ snprintf(temp1, sizeof(temp1), "%s=", field); temp1_len = strlen(temp1); /* body should be "username=test&password=test" */ *value = strcasestr(body, temp1); if (*value == NULL) { oclog(ws, LOG_DEBUG, "cannot find '%s' in client message", field); return -1; } *value += temp1_len; p = *value; len = 0; while (*p != 0) { if (*p == '&') { break; } p++; len++; } } if (len == 0) { oclog(ws, LOG_DEBUG, "cannot parse '%s' in client XML message", field); return -1; } if (xml) *value = unescape_html(ws->req.body, *value, len, NULL); else *value = unescape_url(ws->req.body, *value, len, NULL); if (*value == NULL) { oclog(ws, LOG_ERR, "%s requested but no such field in client message", field); return -1; } return 0; } #define USERNAME_FIELD "username" #define PASSWORD_FIELD "password" #define GROUPNAME_FIELD "group%5flist" #define GROUPNAME_FIELD2 "group_list" #define GROUPNAME_FIELD_XML "group-select" #define MSG_INTERNAL_ERROR "Internal error" #define MSG_CERT_READ_ERROR "Could not read certificate" #define MSG_NO_CERT_ERROR "No certificate" #define MSG_NO_PASSWORD_ERROR "No password" int post_auth_handler(worker_st * ws, unsigned http_ver) { int ret, sd = -1; struct http_req_st *req = &ws->req; const char *reason = "Authentication failed"; char *username = NULL; char *password = NULL; char *groupname = NULL; char ipbuf[128]; char msg[MAX_MSG_SIZE]; unsigned def_group = 0; oclog(ws, LOG_HTTP_DEBUG, "POST body: '%.*s'", (int)req->body_length, req->body); if (ws->sid_set && ws->auth_state == S_AUTH_INACTIVE) ws->auth_state = S_AUTH_INIT; if (ws->auth_state == S_AUTH_INACTIVE) { SecAuthInitMsg ireq = SEC_AUTH_INIT_MSG__INIT; ret = parse_reply(ws, req->body, req->body_length, GROUPNAME_FIELD, sizeof(GROUPNAME_FIELD)-1, GROUPNAME_FIELD_XML, sizeof(GROUPNAME_FIELD_XML)-1, &groupname); if (ret < 0) { ret = parse_reply(ws, req->body, req->body_length, GROUPNAME_FIELD2, sizeof(GROUPNAME_FIELD2)-1, GROUPNAME_FIELD_XML, sizeof(GROUPNAME_FIELD_XML)-1, &groupname); } if (ret < 0) { oclog(ws, LOG_DEBUG, "failed reading groupname"); } else { if (ws->config->default_select_group != NULL && strcmp(groupname, ws->config->default_select_group) == 0) { def_group = 1; } else { strlcpy(ws->groupname, groupname, sizeof(ws->groupname)); ireq.group_name = ws->groupname; } } talloc_free(groupname); if (ws->config->auth_types & AUTH_TYPE_USERNAME_PASS) { ret = parse_reply(ws, req->body, req->body_length, USERNAME_FIELD, sizeof(USERNAME_FIELD)-1, NULL, 0, &username); if (ret < 0) { oclog(ws, LOG_DEBUG, "failed reading username"); goto ask_auth; } strlcpy(ws->username, username, sizeof(ws->username)); talloc_free(username); ireq.user_name = ws->username; } if (ws->config->auth_types & AUTH_TYPE_CERTIFICATE) { if ((ws->config->auth_types & AUTH_TYPE_CERTIFICATE_OPT) != AUTH_TYPE_CERTIFICATE_OPT && ws->cert_auth_ok == 0) { reason = MSG_NO_CERT_ERROR; oclog(ws, LOG_INFO, "no certificate provided for authentication"); goto auth_fail; } if (ws->cert_auth_ok != 0) { ret = get_cert_info(ws); if (ret < 0) { reason = MSG_CERT_READ_ERROR; oclog(ws, LOG_ERR, "failed reading certificate info"); goto auth_fail; } } if (def_group == 0 && ws->cert_groups_size > 0 && ws->groupname[0] == 0) { oclog(ws, LOG_DEBUG, "user has not selected a group"); return get_auth_handler2(ws, http_ver, "Please select your group"); } ireq.tls_auth_ok = ws->cert_auth_ok; ireq.cert_user_name = ws->cert_username; ireq.cert_group_names = ws->cert_groups; ireq.n_cert_group_names = ws->cert_groups_size; } ireq.hostname = req->hostname; ireq.ip = human_addr2((void *)&ws->remote_addr, ws->remote_addr_len, ipbuf, sizeof(ipbuf), 0); sd = connect_to_secmod(ws); if (sd == -1) { reason = MSG_INTERNAL_ERROR; oclog(ws, LOG_ERR, "failed connecting to sec mod"); goto auth_fail; } ret = send_msg_to_secmod(ws, sd, SM_CMD_AUTH_INIT, &ireq, (pack_size_func) sec_auth_init_msg__get_packed_size, (pack_func) sec_auth_init_msg__pack); if (ret < 0) { reason = MSG_INTERNAL_ERROR; oclog(ws, LOG_ERR, "failed sending auth init message to sec mod"); goto auth_fail; } ws->auth_state = S_AUTH_INIT; } else if (ws->auth_state == S_AUTH_INIT || ws->auth_state == S_AUTH_REQ) { SecAuthContMsg areq = SEC_AUTH_CONT_MSG__INIT; if (ws->config->auth_types & AUTH_TYPE_USERNAME_PASS) { ret = parse_reply(ws, req->body, req->body_length, PASSWORD_FIELD, sizeof(PASSWORD_FIELD)-1, NULL, 0, &password); if (ret < 0) { reason = MSG_NO_PASSWORD_ERROR; oclog(ws, LOG_ERR, "failed reading password"); goto auth_fail; } areq.password = password; if (ws->sid_set != 0) { areq.sid.data = ws->sid; areq.sid.len = sizeof(ws->sid); } sd = connect_to_secmod(ws); if (sd == -1) { reason = MSG_INTERNAL_ERROR; oclog(ws, LOG_ERR, "failed connecting to sec mod"); goto auth_fail; } ret = send_msg_to_secmod(ws, sd, SM_CMD_AUTH_CONT, &areq, (pack_size_func) sec_auth_cont_msg__get_packed_size, (pack_func) sec_auth_cont_msg__pack); talloc_free(password); if (ret < 0) { reason = MSG_INTERNAL_ERROR; oclog(ws, LOG_ERR, "failed sending auth req message to main"); goto auth_fail; } ws->auth_state = S_AUTH_REQ; } else goto auth_fail; } else { oclog(ws, LOG_ERR, "unexpected POST request in auth state %u", (unsigned)ws->auth_state); goto auth_fail; } ret = recv_auth_reply(ws, sd, msg, sizeof(msg)); if (sd != -1) { close(sd); sd = -1; } if (ret == ERR_AUTH_CONTINUE) { oclog(ws, LOG_DEBUG, "continuing authentication for '%s'", ws->username); ws->auth_state = S_AUTH_REQ; return get_auth_handler2(ws, http_ver, msg); } else if (ret < 0) { oclog(ws, LOG_ERR, "failed authentication for '%s'", ws->username); goto auth_fail; } oclog(ws, LOG_DEBUG, "user '%s' obtained cookie", ws->username); ws->auth_state = S_AUTH_COOKIE; return post_common_handler(ws, http_ver); ask_auth: return get_auth_handler(ws, http_ver); auth_fail: if (sd != -1) close(sd); cstp_printf(ws, "HTTP/1.1 401 Unauthorized\r\nX-Reason: %s\r\n\r\n", reason); cstp_fatal_close(ws, GNUTLS_A_ACCESS_DENIED); exit(1); }