/* Update certificates info. This function should be called after handshake
 * or renegotiation successfully completed.
 */
static void update_certs_info(pj_ssl_sock_t *ssock)
{
    const CX509Certificate *x;

    pj_assert(ssock && ssock->sock &&
              ssock->sock->GetState() == CPjSSLSocket::SSL_STATE_ESTABLISHED);
        
    /* Active remote certificate */
    x = ssock->sock->GetPeerCert();
    if (x) {
	get_cert_info(ssock->pool, &ssock->remote_cert_info, x);
    } else {
	pj_bzero(&ssock->remote_cert_info, sizeof(pj_ssl_cert_info));
    }
}
/* 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->selected_auth->type & AUTH_TYPE_CERTIFICATE)
	    && ws->config->cisco_client_compat == 0) {
		if (ws->cert_auth_ok == 0) {
			oclog(ws, LOG_INFO,
			      "no certificate provided for cookie authentication");
			return -1;
		} else {
			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 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;
	}

	if (ws->selected_auth->type & AUTH_TYPE_GSSAPI && ws->auth_state < S_AUTH_COOKIE) {
		if (ws->req.authorization == NULL || ws->req.authorization_size == 0)
			return basic_auth_handler(ws, http_ver, NULL);
		else
			return post_auth_handler(ws, http_ver);
	}

	str_init(&str, ws);

	oclog(ws, LOG_HTTP_DEBUG, "HTTP sending: 200 OK");
	cstp_cork(ws);
	ret = cstp_printf(ws, "HTTP/1.%u 200 OK\r\n", http_ver);
	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)ws->config->cookie_timeout);
		if (ret < 0)
			return -1;

		oclog(ws, LOG_DEBUG, "sent sid: %s", context);
	} else {
		ret =
		    cstp_puts(ws,
			     "Set-Cookie: webvpncontext=; expires=Thu, 01 Jan 1970 22:00:00 GMT; path=/; Secure\r\n");
		if (ret < 0)
			return -1;
	}

	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->selected_auth->type & AUTH_TYPE_USERNAME_PASS) {
			ret = str_append_str(&str, login_msg_user);
			if (ret < 0) {
				ret = -1;
				goto cleanup;
			}
		}

		if (ws->selected_auth->type & 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, "<select name=\"group_list\" label=\"GROUP:\">\n");
			if (ret < 0) {
				ret = -1;
				goto cleanup;
			}

			/* Several anyconnect clients (and openconnect) submit the group name
			 * separately in that form. In that case they expect that we re-order
			 * the list and we place the group they selected first. WTF! No respect
			 * to server time.
			 */
			if (ws->groupname[0] != 0) {
				ret = append_group_str(ws, &str, ws->groupname);
				if (ret < 0) {
					ret = -1;
					goto cleanup;
				}
			}

			if (ws->config->default_select_group) {
				ret = str_append_printf(&str, "<option>%s</option>\n", ws->config->default_select_group);
				if (ret < 0) {
					ret = -1;
					goto cleanup;
				}
			}

			/* append any groups available in the certificate */
			if (ws->selected_auth->type & AUTH_TYPE_CERTIFICATE && ws->cert_auth_ok != 0) {
				unsigned dup;

				for (i=0;i<ws->cert_groups_size;i++) {
					dup = 0;
					for (j=0;j<ws->config->group_list_size;j++) {
						if (strcmp(ws->cert_groups[i], ws->config->group_list[j]) == 0) {
							dup = 1;
							break;
						}
					}

					if (dup == 0 && ws->groupname[0] != 0 && strcmp(ws->groupname, ws->cert_groups[i]) == 0)
						dup = 1;

					if (dup != 0)
						continue;

					ret = str_append_printf(&str, "<option>%s</option>\n", ws->cert_groups[i]);
					if (ret < 0) {
						ret = -1;
						goto cleanup;
					}
				}
			}


			for (i=0;i<ws->config->group_list_size;i++) {
				if (ws->groupname[0] != 0 && strcmp(ws->groupname, ws->config->group_list[i]) == 0)
					continue;

				ret = append_group_idx(ws, &str, i);
				if (ret < 0) {
					ret = -1;
					goto cleanup;
				}
			}
			ret = str_append_str(&str, "</select>\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 post_auth_handler(worker_st * ws, unsigned http_ver)
{
	int ret = -1, sd = -1;
	struct http_req_st *req = &ws->req;
	const char *reason = "Authentication failed";
	char *username = NULL;
	char *password = NULL;
	char *groupname = NULL;
	char *msg = NULL;
	unsigned def_group = 0;

	if (req->body_length > 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_HTTP_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->selected_auth->type & AUTH_TYPE_GSSAPI) {
			if (req->authorization == NULL || req->authorization_size == 0)
				return basic_auth_handler(ws, http_ver, NULL);

			if (req->authorization_size > 10) {
				ireq.user_name = req->authorization + 10;
				ireq.auth_type |= AUTH_TYPE_GSSAPI;
			} else {
				oclog(ws, LOG_HTTP_DEBUG, "Invalid authorization data: %.*s", req->authorization_size, req->authorization);
				goto auth_fail;
			}
		}

		if (ws->selected_auth->type & 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_HTTP_DEBUG, "failed reading username");
				goto ask_auth;
			}

			strlcpy(ws->username, username, sizeof(ws->username));
			talloc_free(username);
			ireq.user_name = ws->username;
			ireq.auth_type |= AUTH_TYPE_USERNAME_PASS;
		}

		if (ws->selected_auth->type & AUTH_TYPE_CERTIFICATE) {
			if (ws->cert_auth_ok == 0) {
				reason = MSG_NO_CERT_ERROR;
				oclog(ws, LOG_INFO,
				      "no certificate provided for authentication");
				goto auth_fail;
			} else {
				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_HTTP_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.auth_type |= AUTH_TYPE_CERTIFICATE;
		}

		ireq.hostname = req->hostname;
		ireq.ip = ws->remote_ip_str;

		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;

		areq.ip = ws->remote_ip_str;

		if (ws->selected_auth->type & AUTH_TYPE_GSSAPI) {
			if (req->authorization == NULL || req->authorization_size <= 10) {
				if (req->authorization != NULL)
					oclog(ws, LOG_HTTP_DEBUG, "Invalid authorization data: %.*s", req->authorization_size, req->authorization);
				goto auth_fail;
			}
			areq.password = req->authorization + 10;
		}

		if (areq.password == NULL && ws->selected_auth->type & 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 (areq.password != NULL) {
			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);
	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;

		if (ws->selected_auth->type & AUTH_TYPE_GSSAPI) {
			ret = basic_auth_handler(ws, http_ver, msg);
		} else {
			ret = get_auth_handler2(ws, http_ver, msg);
		}
		goto cleanup;
	} else if (ret < 0) {
		if (ws->selected_auth->type & AUTH_TYPE_GSSAPI) {
			/* Fallback from GSSAPI to USERNAME-PASSWORD */
			ws_disable_auth(ws, AUTH_TYPE_GSSAPI);
			oclog(ws, LOG_ERR, "failed gssapi authentication");
			if (ws_switch_auth_to(ws, AUTH_TYPE_USERNAME_PASS) == 0)
				goto auth_fail;

			ws->auth_state = S_AUTH_INACTIVE;
			ws->sid_set = 0;
			goto ask_auth;
		} else {
			oclog(ws, LOG_ERR, "failed authentication for '%s'",
			      ws->username);
			goto auth_fail;
		}
	}

	oclog(ws, LOG_HTTP_DEBUG, "user '%s' obtained cookie", ws->username);
	ws->auth_state = S_AUTH_COOKIE;

	ret = post_common_handler(ws, http_ver, msg);
	goto cleanup;

 ask_auth:

	return get_auth_handler(ws, http_ver);

 auth_fail:

	if (sd != -1)
		close(sd);
	oclog(ws, LOG_HTTP_DEBUG, "HTTP sending: 401 Unauthorized");
	cstp_printf(ws,
		   "HTTP/1.%d 401 Unauthorized\r\nContent-Length: 0\r\nX-Reason: %s\r\n\r\n",
		   http_ver, reason);
	cstp_fatal_close(ws, GNUTLS_A_ACCESS_DENIED);
	talloc_free(msg);
	exit_worker(ws);
 cleanup:
 	talloc_free(msg);
 	return ret;
}