mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-02-09 08:16:58 +08:00
Added the select-group and auto-select-group config options.
These options allow to prompt the user for a group prior to login. That in addition enhances the password file format and multiple groups can be specified on a comma separated list, as: user:group1,group2,group3:$5$encodedpassword
This commit is contained in:
committed by
Nikos Mavrogiannopoulos
parent
6bc625df81
commit
4755ee48c5
@@ -271,6 +271,19 @@ route = 192.168.5.0/255.255.255.0
|
||||
#default-user-config = /etc/ocserv/defaults/user.conf
|
||||
#default-group-config = /etc/ocserv/defaults/group.conf
|
||||
|
||||
# Groups that a client is allowed to select from.
|
||||
# A client may belong in multiple groups, and in certain use-cases
|
||||
# it is needed to switch between them. For these cases the client can
|
||||
# select prior to authentication. Add multiple entries for multiple groups.
|
||||
#select-group = group1
|
||||
#select-group = group2
|
||||
#select-group = tost
|
||||
|
||||
# Instead of specifying manually all the allowed groups, you may instruct
|
||||
# ocserv to scan all available groups and include the full list. That
|
||||
# option is only functional on plain authentication.
|
||||
#auto-select-group = true
|
||||
|
||||
# The system command to use to setup a route. %R will be replaced with the
|
||||
# route/mask and %D with the (tun) device.
|
||||
#
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
test:tost:$5$i6SNmLDCgBNjyJ7q$SZ4bVJb7I/DLgXo3txHBVohRFBjOtdbxGQZp.DOnrA.
|
||||
test:tost , group1 , group2:$5$i6SNmLDCgBNjyJ7q$SZ4bVJb7I/DLgXo3txHBVohRFBjOtdbxGQZp.DOnrA.
|
||||
tost:*:$5$qc1CKWjcmlrFREe2$h9zQJcD3AeuQmwMncdIqqls47TDOUvu2r2nXQ7aDfcD
|
||||
|
||||
@@ -264,17 +264,40 @@ struct pam_ctx_st * pctx = ctx;
|
||||
|
||||
/* Returns 0 if the user is successfully authenticated
|
||||
*/
|
||||
static int pam_auth_group(void* ctx, char *groupname, int groupname_size)
|
||||
static int pam_auth_group(void* ctx, const char *suggested, char *groupname, int groupname_size)
|
||||
{
|
||||
struct passwd * pwd;
|
||||
struct pam_ctx_st * pctx = ctx;
|
||||
struct group *grp;
|
||||
int ret;
|
||||
|
||||
groupname[0] = 0;
|
||||
|
||||
pwd = getpwnam(pctx->username);
|
||||
if (pwd != NULL) {
|
||||
struct group* grp = getgrgid(pwd->pw_gid);
|
||||
if (grp != NULL)
|
||||
snprintf(groupname, groupname_size, "%s", grp->gr_name);
|
||||
if (suggested != NULL) {
|
||||
gid_t groups[MAX_GROUPS];
|
||||
int ngroups = sizeof(groups)/sizeof(groups[0]);
|
||||
unsigned i;
|
||||
|
||||
ret = getgrouplist(pctx->username, pwd->pw_gid, groups, &ngroups);
|
||||
if (ret <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i=0;i<ngroups;i++) {
|
||||
grp = getgrgid(groups[i]);
|
||||
if (grp != NULL && strcmp(suggested, grp->gr_name) == 0) {
|
||||
snprintf(groupname, groupname_size, "%s", grp->gr_name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
struct group* grp = getgrgid(pwd->pw_gid);
|
||||
if (grp != NULL)
|
||||
snprintf(groupname, groupname_size, "%s", grp->gr_name);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -322,7 +345,8 @@ const struct auth_mod_st pam_auth_funcs = {
|
||||
.auth_msg = pam_auth_msg,
|
||||
.auth_pass = pam_auth_pass,
|
||||
.auth_group = pam_auth_group,
|
||||
.auth_user = pam_auth_user
|
||||
.auth_user = pam_auth_user,
|
||||
.group_list = NULL
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
181
src/auth/plain.c
181
src/auth/plain.c
@@ -24,7 +24,10 @@
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <vpn.h>
|
||||
#include <c-ctype.h>
|
||||
#include "plain.h"
|
||||
#include <ccan/htable/htable.h>
|
||||
#include <ccan/hash/hash.h>
|
||||
|
||||
#define MAX_CPASS_SIZE 128
|
||||
#define MAX_TRIES 3
|
||||
@@ -35,17 +38,66 @@ const char* pass_msg_failed = "Login failed.\nPlease enter your password.";
|
||||
struct plain_ctx_st {
|
||||
char username[MAX_USERNAME_SIZE];
|
||||
char cpass[MAX_CPASS_SIZE]; /* crypt() passwd */
|
||||
char groupname[MAX_GROUPNAME_SIZE];
|
||||
|
||||
char *groupnames[MAX_GROUPS];
|
||||
unsigned groupnames_size;
|
||||
|
||||
const char *passwd; /* password file */
|
||||
const char *pass_msg;
|
||||
unsigned retries;
|
||||
};
|
||||
|
||||
/* Breaks a list of "xxx", "yyy", to a character array, of
|
||||
* MAX_COMMA_SEP_ELEMENTS size; Note that the given string is modified.
|
||||
*/
|
||||
static void
|
||||
break_group_list(void *pool, char *text,
|
||||
char *broken_text[MAX_GROUPS], unsigned *elements)
|
||||
{
|
||||
char *p = talloc_strdup(pool, text);
|
||||
char *p2;
|
||||
unsigned len;
|
||||
|
||||
*elements = 0;
|
||||
|
||||
if (p == NULL)
|
||||
return;
|
||||
|
||||
do {
|
||||
broken_text[*elements] = p;
|
||||
(*elements)++;
|
||||
|
||||
p = strchr(p, ',');
|
||||
if (p) {
|
||||
*p = 0;
|
||||
len = p - broken_text[*elements-1];
|
||||
|
||||
/* remove any trailing space */
|
||||
p2 = p-1;
|
||||
while (c_isspace(*p2)) {
|
||||
*p2 = 0;
|
||||
p2--;
|
||||
}
|
||||
|
||||
p++; /* move to next entry and skip white
|
||||
* space.
|
||||
*/
|
||||
while (c_isspace(*p))
|
||||
p++;
|
||||
|
||||
if (len == 1) {
|
||||
/* skip the group */
|
||||
(*elements)--;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (p != NULL && *elements < MAX_GROUPS);
|
||||
}
|
||||
|
||||
/* Returns 0 if the user is successfully authenticated, and sets the appropriate group name.
|
||||
*/
|
||||
static int read_auth_pass(struct plain_ctx_st *pctx)
|
||||
{
|
||||
unsigned groupname_size;
|
||||
FILE *fp;
|
||||
char line[512];
|
||||
ssize_t ll;
|
||||
@@ -81,12 +133,7 @@ static int read_auth_pass(struct plain_ctx_st *pctx)
|
||||
if (p != NULL && strcmp(pctx->username, p) == 0) {
|
||||
p = strtok_r(NULL, ":", &sp);
|
||||
if (p != NULL) {
|
||||
groupname_size = sizeof(pctx->groupname);
|
||||
groupname_size =
|
||||
snprintf(pctx->groupname, groupname_size,
|
||||
"%s", p);
|
||||
if (groupname_size == 1) /* values like '*' or 'x' indicate empty group */
|
||||
pctx->groupname[0] = 0;
|
||||
break_group_list(pctx, p, pctx->groupnames, &pctx->groupnames_size);
|
||||
|
||||
p = strtok_r(NULL, ":", &sp);
|
||||
if (p != NULL) {
|
||||
@@ -118,10 +165,7 @@ static int plain_auth_init(void **ctx, void *pool, const char *username, const c
|
||||
return ERR_AUTH_FAIL;
|
||||
|
||||
snprintf(pctx->username, sizeof(pctx->username), "%s", username);
|
||||
pctx->groupname[0] = 0;
|
||||
pctx->cpass[0] = 0;
|
||||
pctx->passwd = additional;
|
||||
pctx->retries = 0;
|
||||
pctx->pass_msg = pass_msg_first;
|
||||
|
||||
ret = read_auth_pass(pctx);
|
||||
@@ -135,12 +179,25 @@ static int plain_auth_init(void **ctx, void *pool, const char *username, const c
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int plain_auth_group(void *ctx, char *groupname, int groupname_size)
|
||||
static int plain_auth_group(void *ctx, const char *suggested, char *groupname, int groupname_size)
|
||||
{
|
||||
struct plain_ctx_st *pctx = ctx;
|
||||
unsigned i;
|
||||
|
||||
snprintf(groupname, groupname_size, "%s", pctx->groupname);
|
||||
groupname[0] = 0;
|
||||
|
||||
if (suggested != NULL) {
|
||||
for (i=0;i<pctx->groupnames_size;i++) {
|
||||
if (strcmp(suggested, pctx->groupnames[i]) == 0) {
|
||||
snprintf(groupname, groupname_size, "%s", pctx->groupnames[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pctx->groupnames_size > 0 && groupname[0] == 0) {
|
||||
snprintf(groupname, groupname_size, "%s", pctx->groupnames[0]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -185,6 +242,101 @@ static void plain_auth_deinit(void *ctx)
|
||||
talloc_free(ctx);
|
||||
}
|
||||
|
||||
static size_t rehash(const void *_e, void *unused)
|
||||
{
|
||||
const char *e = _e;
|
||||
return hash_any(e, strlen(e), 0);
|
||||
}
|
||||
|
||||
static bool str_cmp(const void* _c1, void* _c2)
|
||||
{
|
||||
const char *c1 = _c1, *c2 = c2;
|
||||
|
||||
if (strcmp(c1, c2) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void plain_group_list(void *pool, void *additional, char ***groupname, unsigned *groupname_size)
|
||||
{
|
||||
FILE *fp;
|
||||
char line[512];
|
||||
ssize_t ll;
|
||||
char *p, *sp;
|
||||
unsigned i;
|
||||
size_t hval;
|
||||
struct htable_iter iter;
|
||||
char *tgroup[MAX_GROUPS];
|
||||
unsigned tgroup_size;
|
||||
struct htable hash;
|
||||
|
||||
htable_init(&hash, rehash, NULL);
|
||||
|
||||
pool = talloc_init("plain");
|
||||
fp = fopen(additional, "r");
|
||||
if (fp == NULL) {
|
||||
syslog(LOG_AUTH,
|
||||
"error in plain authentication; cannot open: %s",
|
||||
(char*)additional);
|
||||
return;
|
||||
}
|
||||
|
||||
line[sizeof(line)-1] = 0;
|
||||
while ((p=fgets(line, sizeof(line)-1, fp)) != NULL) {
|
||||
ll = strlen(p);
|
||||
|
||||
if (ll <= 4)
|
||||
continue;
|
||||
|
||||
if (line[ll - 1] == '\n') {
|
||||
ll--;
|
||||
line[ll] = 0;
|
||||
}
|
||||
if (line[ll - 1] == '\r') {
|
||||
ll--;
|
||||
line[ll] = 0;
|
||||
}
|
||||
|
||||
p = strtok_r(line, ":", &sp);
|
||||
|
||||
if (p != NULL) {
|
||||
p = strtok_r(NULL, ":", &sp);
|
||||
if (p != NULL) {
|
||||
break_group_list(pool, p, tgroup, &tgroup_size);
|
||||
|
||||
for (i=0;i<tgroup_size;i++) {
|
||||
hval = rehash(tgroup[i], NULL);
|
||||
|
||||
if (htable_get(&hash, hval, str_cmp, tgroup[i]) == NULL) {
|
||||
if (strlen(tgroup[i]) > 1)
|
||||
htable_add(&hash, hval, tgroup[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*groupname_size = 0;
|
||||
*groupname = talloc_size(pool, sizeof(char*)*MAX_GROUPS);
|
||||
if (*groupname == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
p = htable_first(&hash, &iter);
|
||||
while (p != NULL) {
|
||||
(*groupname)[(*groupname_size)] = talloc_strdup(*groupname, p);
|
||||
p = htable_next(&hash, &iter);
|
||||
(*groupname_size)++;
|
||||
}
|
||||
|
||||
/* always succeed */
|
||||
exit:
|
||||
htable_clear(&hash);
|
||||
safe_memset(line, 0, sizeof(line));
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
const struct auth_mod_st plain_auth_funcs = {
|
||||
.type = AUTH_TYPE_PLAIN | AUTH_TYPE_USERNAME_PASS,
|
||||
.auth_init = plain_auth_init,
|
||||
@@ -192,5 +344,6 @@ const struct auth_mod_st plain_auth_funcs = {
|
||||
.auth_msg = plain_auth_msg,
|
||||
.auth_pass = plain_auth_pass,
|
||||
.auth_user = plain_auth_user,
|
||||
.auth_group = plain_auth_group
|
||||
.auth_group = plain_auth_group,
|
||||
.group_list = plain_group_list
|
||||
};
|
||||
|
||||
20
src/config.c
20
src/config.c
@@ -30,8 +30,11 @@
|
||||
#include <limits.h>
|
||||
#include <common.h>
|
||||
#include <c-strcase.h>
|
||||
#include <auth/pam.h>
|
||||
#include <auth/plain.h>
|
||||
|
||||
#include <vpn.h>
|
||||
#include <main.h>
|
||||
#include <ctl.h>
|
||||
#include <tlslib.h>
|
||||
|
||||
@@ -51,6 +54,7 @@ struct cfg_options {
|
||||
static struct cfg_options available_options[] = {
|
||||
{ .name = "auth", .type = OPTION_MULTI_LINE, .mandatory = 1 },
|
||||
{ .name = "route", .type = OPTION_MULTI_LINE, .mandatory = 0 },
|
||||
{ .name = "select-group", .type = OPTION_MULTI_LINE, .mandatory = 0 },
|
||||
{ .name = "custom-header", .type = OPTION_MULTI_LINE, .mandatory = 0 },
|
||||
{ .name = "split-dns", .type = OPTION_MULTI_LINE, .mandatory = 0 },
|
||||
{ .name = "listen-host", .type = OPTION_STRING, .mandatory = 0 },
|
||||
@@ -79,6 +83,7 @@ static struct cfg_options available_options[] = {
|
||||
{ .name = "occtl-socket-file", .type = OPTION_STRING, .mandatory = 0 },
|
||||
{ .name = "banner", .type = OPTION_STRING, .mandatory = 0 },
|
||||
{ .name = "predictable-ips", .type = OPTION_BOOLEAN, .mandatory = 0 },
|
||||
{ .name = "auto-select-group", .type = OPTION_BOOLEAN, .mandatory = 0 },
|
||||
/* this is alias for cisco-client-compat */
|
||||
{ .name = "always-require-cert", .type = OPTION_BOOLEAN, .mandatory = 0 },
|
||||
{ .name = "cisco-client-compat", .type = OPTION_BOOLEAN, .mandatory = 0 },
|
||||
@@ -257,7 +262,8 @@ const tOptionValue* val, *prev;
|
||||
unsigned j, i, mand;
|
||||
char** auth = NULL;
|
||||
unsigned auth_size = 0;
|
||||
unsigned prefix = 0;
|
||||
unsigned prefix = 0, auto_select_group = 0;
|
||||
const struct auth_mod_st *amod = NULL;
|
||||
char *tmp;
|
||||
unsigned force_cert_auth;
|
||||
|
||||
@@ -294,6 +300,7 @@ unsigned force_cert_auth;
|
||||
}
|
||||
#ifdef HAVE_PAM
|
||||
config->auth_types |= AUTH_TYPE_PAM;
|
||||
amod = &pam_auth_funcs;
|
||||
#else
|
||||
fprintf(stderr, "PAM support is disabled\n");
|
||||
exit(1);
|
||||
@@ -313,6 +320,7 @@ unsigned force_cert_auth;
|
||||
exit(1);
|
||||
}
|
||||
*p = 0;
|
||||
amod = &plain_auth_funcs;
|
||||
config->auth_types |= AUTH_TYPE_PLAIN;
|
||||
} else if (c_strcasecmp(auth[j], "certificate") == 0) {
|
||||
config->auth_types |= AUTH_TYPE_CERTIFICATE;
|
||||
@@ -492,6 +500,13 @@ unsigned force_cert_auth;
|
||||
}
|
||||
}
|
||||
|
||||
READ_TF("auto-select-group", auto_select_group, 0);
|
||||
if (auto_select_group != 0 && amod != NULL && amod->group_list != NULL) {
|
||||
amod->group_list(config, config->plain_passwd, &config->group_list, &config->group_list_size);
|
||||
} else {
|
||||
READ_MULTI_LINE("select-group", config->group_list, config->group_list_size);
|
||||
}
|
||||
|
||||
READ_MULTI_LINE("dns", config->network.dns, config->network.dns_size);
|
||||
if (config->network.dns_size == 0) {
|
||||
/* try the aliases */
|
||||
@@ -691,6 +706,9 @@ unsigned i;
|
||||
for (i=0;i<config->split_dns_size;i++)
|
||||
DEL(config->split_dns[i]);
|
||||
DEL(config->split_dns);
|
||||
for (i=0;i<config->group_list_size;i++)
|
||||
DEL(config->group_list[i]);
|
||||
DEL(config->group_list);
|
||||
#ifdef HAVE_LIBTALLOC
|
||||
/* our included talloc don't include that */
|
||||
talloc_free_children(config);
|
||||
|
||||
@@ -129,10 +129,11 @@ message sec_auth_init_msg
|
||||
required bool user_present = 1;
|
||||
required bool tls_auth_ok = 2 [default = false];
|
||||
required string user_name = 3;
|
||||
optional string cert_user_name = 4;
|
||||
optional string cert_group_name = 5;
|
||||
optional string hostname = 6;
|
||||
required string ip = 7;
|
||||
optional string group_name = 4; /* selected group name */
|
||||
optional string cert_user_name = 5;
|
||||
optional string cert_group_name = 6;
|
||||
optional string hostname = 7;
|
||||
required string ip = 8;
|
||||
}
|
||||
|
||||
/* SEC_AUTH_CONT */
|
||||
|
||||
@@ -754,7 +754,7 @@ unsigned total = 10;
|
||||
|
||||
if (reload_conf != 0) {
|
||||
mslog(s, NULL, LOG_INFO, "reloading configuration");
|
||||
reload_cfg_file(s->main_pool, &s->config);
|
||||
reload_cfg_file(s->main_pool, s->config);
|
||||
tls_reload_crl(s, s->creds);
|
||||
reload_conf = 0;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include <common.h>
|
||||
|
||||
int cmd_parser (void *pool, int argc, char **argv, struct cfg_st** config);
|
||||
void reload_cfg_file(void *pool, struct cfg_st** config);
|
||||
void reload_cfg_file(void *pool, struct cfg_st* config);
|
||||
void clear_cfg_file(struct cfg_st* config);
|
||||
void write_pid_file(void);
|
||||
void remove_pid_file(void);
|
||||
|
||||
@@ -345,6 +345,18 @@ route = 192.168.1.0/255.255.255.0
|
||||
route = 192.168.5.0/255.255.255.0
|
||||
#route = fef4:db8:1000:1001::/64
|
||||
|
||||
# Groups that a client is allowed to select from.
|
||||
# A client may belong in multiple groups, and in certain use-cases
|
||||
# it is needed to switch between them. For these cases the client can
|
||||
# select prior to authentication. Add multiple entries for multiple groups.
|
||||
#select-group = group1
|
||||
#select-group = group2
|
||||
|
||||
# Instead of specifying manually all the allowed groups, you may instruct
|
||||
# ocserv to scan all available groups and include the full list. That
|
||||
# option is only functional on plain authentication.
|
||||
#auto-select-group = true
|
||||
|
||||
# Configuration files that will be applied per user connection or
|
||||
# per group. Each file name on these directories must match the username
|
||||
# or the groupname.
|
||||
|
||||
@@ -353,7 +353,6 @@ int handle_sec_auth_init(sec_mod_st * sec, const SecAuthInitMsg * req)
|
||||
snprintf(e->hostname, sizeof(e->hostname), "%s", req->hostname);
|
||||
}
|
||||
|
||||
|
||||
if (sec->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
|
||||
/* req->username is non-null at this point */
|
||||
ret =
|
||||
@@ -364,7 +363,7 @@ int handle_sec_auth_init(sec_mod_st * sec, const SecAuthInitMsg * req)
|
||||
}
|
||||
|
||||
ret =
|
||||
module->auth_group(e->auth_ctx, e->groupname,
|
||||
module->auth_group(e->auth_ctx, req->group_name, e->groupname,
|
||||
sizeof(e->groupname));
|
||||
if (ret != 0)
|
||||
return -1;
|
||||
|
||||
@@ -24,15 +24,17 @@
|
||||
#include <main.h>
|
||||
|
||||
#define MAX_AUTH_REQS 8
|
||||
#define MAX_GROUPS 32
|
||||
|
||||
struct auth_mod_st {
|
||||
unsigned int type;
|
||||
int (*auth_init)(void** ctx, void *pool, const char* username, const char* ip, void* additional);
|
||||
int (*auth_msg)(void* ctx, char* msg, size_t msg_size);
|
||||
int (*auth_pass)(void* ctx, const char* pass, unsigned pass_len);
|
||||
int (*auth_group)(void* ctx, char *groupname, int groupname_size);
|
||||
int (*auth_group)(void* ctx, const char *suggested, char *groupname, int groupname_size);
|
||||
int (*auth_user)(void* ctx, char *groupname, int groupname_size);
|
||||
void (*auth_deinit)(void* ctx);
|
||||
void (*group_list)(void *pool, void *additional, char ***groupname, unsigned *groupname_size);
|
||||
};
|
||||
|
||||
void main_auth_init(main_server_st *s);
|
||||
|
||||
@@ -199,6 +199,9 @@ struct cfg_st {
|
||||
char *ocsp_response; /* file with the OCSP response */
|
||||
char *default_domain; /* domain to be advertised */
|
||||
|
||||
char **group_list; /* select_group */
|
||||
unsigned int group_list_size;
|
||||
|
||||
char **custom_header;
|
||||
unsigned custom_header_size;;
|
||||
|
||||
|
||||
@@ -54,22 +54,27 @@
|
||||
|
||||
#define SUCCESS_MSG_FOOT "</auth></config-auth>\n"
|
||||
|
||||
static const char login_msg_user[] =
|
||||
static const char login_msg_user_start[] =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
"<config-auth client=\"vpn\" type=\"auth-request\">\n"
|
||||
VERSION_MSG
|
||||
"<auth id=\"main\">\n"
|
||||
"<message>Please enter your username</message>\n"
|
||||
"<form method=\"post\" action=\"/auth\">\n"
|
||||
"<input type=\"text\" name=\"username\" label=\"Username:\" />\n"
|
||||
"<input type=\"text\" name=\"username\" label=\"Username:\" />\n";
|
||||
|
||||
static const char login_msg_user_end[] =
|
||||
"</form></auth>\n" "</config-auth>";
|
||||
|
||||
static const char login_msg_no_user[] =
|
||||
static const char login_msg_no_user_start[] =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
"<config-auth client=\"vpn\" type=\"auth-request\">\n"
|
||||
VERSION_MSG
|
||||
"<auth id=\"main\">\n"
|
||||
"<message>%s</message>\n"
|
||||
"<message>";
|
||||
|
||||
static const char login_msg_no_user_end[] =
|
||||
"</message>\n"
|
||||
"<form method=\"post\" action=\"/auth\">\n"
|
||||
"<input type=\"password\" name=\"password\" label=\"Password:\" />\n"
|
||||
"</form></auth></config-auth>\n";
|
||||
@@ -77,9 +82,12 @@ static const char login_msg_no_user[] =
|
||||
int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg)
|
||||
{
|
||||
int ret;
|
||||
char login_msg[MAX_MSG_SIZE + sizeof(login_msg_user)];
|
||||
char context[BASE64_LENGTH(SID_SIZE) + 1];
|
||||
unsigned int lsize;
|
||||
char temp[128];
|
||||
unsigned int i;
|
||||
str_st str;
|
||||
|
||||
str_init(&str, ws);
|
||||
|
||||
tls_cork(ws->session);
|
||||
ret = tls_printf(ws->session, "HTTP/1.%u 200 OK\r\n", http_ver);
|
||||
@@ -105,46 +113,111 @@ int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg)
|
||||
}
|
||||
|
||||
ret = tls_puts(ws->session, "Content-Type: text/xml\r\n");
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
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.";
|
||||
lsize =
|
||||
snprintf(login_msg, sizeof(login_msg), login_msg_no_user,
|
||||
pmsg);
|
||||
|
||||
ret = str_append_str(&str, login_msg_no_user_start);
|
||||
if (ret < 0) {
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = str_append_str(&str, pmsg);
|
||||
if (ret < 0) {
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = str_append_str(&str, login_msg_no_user_end);
|
||||
if (ret < 0) {
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* ask for username only */
|
||||
lsize =
|
||||
snprintf(login_msg, sizeof(login_msg), "%s",
|
||||
login_msg_user);
|
||||
/* ask for username and group */
|
||||
ret = str_append_str(&str, login_msg_user_start);
|
||||
if (ret < 0) {
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* send groups */
|
||||
if (ws->groupname[0] == 0 && ws->config->group_list_size > 0) {
|
||||
ret = str_append_str(&str, "<select name=\"group_list\" label=\"GROUP:\">\n");
|
||||
if (ret < 0) {
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (i=0;i<ws->config->group_list_size;i++) {
|
||||
snprintf(temp, sizeof(temp), "<option>%s</option>\n", ws->config->group_list[i]);
|
||||
ret = str_append_str(&str, temp);
|
||||
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_user_end);
|
||||
if (ret < 0) {
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ret =
|
||||
tls_printf(ws->session, "Content-Length: %u\r\n",
|
||||
(unsigned int)lsize);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
(unsigned int)str.length);
|
||||
if (ret < 0) {
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = tls_puts(ws->session, "X-Transcend-Version: 1\r\n");
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
if (ret < 0) {
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = tls_puts(ws->session, "\r\n");
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
if (ret < 0) {
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = tls_send(ws->session, str.data, str.length);
|
||||
if (ret < 0) {
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = tls_send(ws->session, login_msg, lsize);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
ret = tls_uncork(ws->session);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
if (ret < 0) {
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
return 0;
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
str_clear(&str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int get_auth_handler(worker_st * ws, unsigned http_ver)
|
||||
@@ -670,151 +743,93 @@ int post_common_handler(worker_st * ws, unsigned http_ver)
|
||||
* buffers.
|
||||
*/
|
||||
static
|
||||
int read_user_pass(worker_st * ws, char *body, unsigned body_length,
|
||||
char **username, char **password)
|
||||
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;
|
||||
|
||||
if (memmem(body, body_length, "<?xml", 5) != 0) {
|
||||
|
||||
if (username != NULL) {
|
||||
/* body should contain <username>test</username><password>test</password> */
|
||||
*username =
|
||||
memmem(body, body_length, XMLUSER,
|
||||
sizeof(XMLUSER) - 1);
|
||||
if (*username == NULL) {
|
||||
oclog(ws, LOG_DEBUG,
|
||||
"cannot find username in client XML message");
|
||||
return -1;
|
||||
}
|
||||
*username += sizeof(XMLUSER) - 1;
|
||||
if (xml_field) {
|
||||
field = xml_field;
|
||||
field_size = xml_field_size;
|
||||
}
|
||||
|
||||
if (password != NULL) {
|
||||
*password =
|
||||
memmem(body, body_length, XMLPASS,
|
||||
sizeof(XMLPASS) - 1);
|
||||
if (*password == NULL) {
|
||||
oclog(ws, LOG_DEBUG,
|
||||
"cannot find password in client XML message");
|
||||
return -1;
|
||||
}
|
||||
*password += sizeof(XMLPASS) - 1;
|
||||
temp1_len = snprintf(temp1, sizeof(temp1), "<%s>", field);
|
||||
temp2_len = snprintf(temp2, sizeof(temp2), "</%s>", field);
|
||||
|
||||
/* body should contain <username>test</username><password>test</password> */
|
||||
*value =
|
||||
memmem(body, body_length, temp1, temp1_len);
|
||||
if (*value == NULL) {
|
||||
oclog(ws, LOG_DEBUG,
|
||||
"cannot find '%s' in client XML message", field);
|
||||
return -1;
|
||||
}
|
||||
*value += temp1_len;
|
||||
|
||||
/* modify body */
|
||||
if (username != NULL) {
|
||||
p = *username;
|
||||
while (*p != 0) {
|
||||
if (*p == '<'
|
||||
&&
|
||||
(strncmp
|
||||
(p, XMLUSER_END,
|
||||
sizeof(XMLUSER_END) - 1) == 0)) {
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
p = *value;
|
||||
len = 0;
|
||||
while (*p != 0) {
|
||||
if (*p == '<'
|
||||
&& (strncmp(p, temp2, temp2_len) == 0)) {
|
||||
break;
|
||||
}
|
||||
|
||||
*username =
|
||||
unescape_html(ws, *username, strlen(*username),
|
||||
NULL);
|
||||
p++;
|
||||
len++;
|
||||
}
|
||||
|
||||
if (password != NULL) {
|
||||
p = *password;
|
||||
while (*p != 0) {
|
||||
if (*p == '<'
|
||||
&&
|
||||
(strncmp
|
||||
(p, XMLPASS_END,
|
||||
sizeof(XMLPASS_END) - 1) == 0)) {
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
|
||||
}
|
||||
|
||||
*password =
|
||||
unescape_html(ws, *password, strlen(*password),
|
||||
NULL);
|
||||
}
|
||||
|
||||
} else { /* non-xml version */
|
||||
temp1_len = snprintf(temp1, sizeof(temp1), "%s=", field);
|
||||
|
||||
/* body should be "username=test&password=test" */
|
||||
if (username != NULL) {
|
||||
*username =
|
||||
memmem(body, body_length, "username=",
|
||||
sizeof("username=") - 1);
|
||||
if (*username == NULL) {
|
||||
oclog(ws, LOG_DEBUG,
|
||||
"cannot find username in client message");
|
||||
return -1;
|
||||
}
|
||||
*username += sizeof("username=") - 1;
|
||||
*value =
|
||||
memmem(body, body_length, temp1, temp1_len);
|
||||
if (*value == NULL) {
|
||||
oclog(ws, LOG_DEBUG,
|
||||
"cannot find '%s' in client message", field);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (password != NULL) {
|
||||
*password =
|
||||
memmem(body, body_length, "password=",
|
||||
sizeof("password=") - 1);
|
||||
if (*password == NULL) {
|
||||
oclog(ws, LOG_DEBUG,
|
||||
"cannot find password in client message");
|
||||
return -1;
|
||||
*value += temp1_len;
|
||||
|
||||
p = *value;
|
||||
len = 0;
|
||||
while (*p != 0) {
|
||||
if (*p == '&') {
|
||||
break;
|
||||
}
|
||||
*password += sizeof("password=") - 1;
|
||||
}
|
||||
|
||||
/* modify body */
|
||||
if (username != NULL) {
|
||||
p = *username;
|
||||
while (*p != 0) {
|
||||
if (*p == '&') {
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
*username =
|
||||
unescape_url(ws, *username, strlen(*username),
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (password != NULL) {
|
||||
p = *password;
|
||||
while (*p != 0) {
|
||||
if (*p == '&') {
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
*password =
|
||||
unescape_url(ws, *password, strlen(*password),
|
||||
NULL);
|
||||
p++;
|
||||
len++;
|
||||
}
|
||||
}
|
||||
|
||||
if (username != NULL && *username == NULL) {
|
||||
oclog(ws, LOG_ERR,
|
||||
"username requested but no username in client message");
|
||||
if (len == 0) {
|
||||
oclog(ws, LOG_DEBUG,
|
||||
"cannot parse '%s' in client XML message", field);
|
||||
return -1;
|
||||
}
|
||||
*value =
|
||||
unescape_url(ws->req.body, *value, len, NULL);
|
||||
|
||||
if (password != NULL && *password == NULL) {
|
||||
if (*value == NULL) {
|
||||
oclog(ws, LOG_ERR,
|
||||
"password requested but no password in client message");
|
||||
"%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_FIELD_XML "group-select"
|
||||
|
||||
int post_auth_handler(worker_st * ws, unsigned http_ver)
|
||||
{
|
||||
int ret, sd = -1;
|
||||
@@ -822,6 +837,7 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
|
||||
const char *reason = "Authentication failed";
|
||||
char *username = NULL;
|
||||
char *password = NULL;
|
||||
char *groupname = NULL;
|
||||
char tmp_user[MAX_USERNAME_SIZE];
|
||||
char tmp_group[MAX_USERNAME_SIZE];
|
||||
char ipbuf[128];
|
||||
@@ -830,71 +846,31 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
|
||||
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;
|
||||
|
||||
if (ws->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
|
||||
ret =
|
||||
read_user_pass(ws, req->body, req->body_length,
|
||||
&username, NULL);
|
||||
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) {
|
||||
/* No username, see if we are continuing a previous session */
|
||||
if (ws->config->cisco_client_compat != 0 &&
|
||||
gnutls_session_is_resumed(ws->session) !=
|
||||
0) {
|
||||
SecAuthContMsg rreq =
|
||||
SEC_AUTH_CONT_MSG__INIT;
|
||||
|
||||
/* could it be a client reconnecting and sending
|
||||
* his password? */
|
||||
ret =
|
||||
read_user_pass(ws, req->body,
|
||||
req->body_length,
|
||||
NULL, &password);
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_INFO,
|
||||
"failed reading password as well");
|
||||
goto ask_auth;
|
||||
}
|
||||
|
||||
rreq.tls_auth_ok = ws->cert_auth_ok;
|
||||
rreq.password = password;
|
||||
rreq.ip =
|
||||
human_addr2((void *)&ws->remote_addr, ws->remote_addr_len,
|
||||
ipbuf, sizeof(ipbuf), 0);
|
||||
|
||||
if (ws->sid_set != 0) {
|
||||
rreq.sid.data = ws->sid;
|
||||
rreq.sid.len = sizeof(ws->sid);
|
||||
}
|
||||
|
||||
sd = connect_to_secmod(ws);
|
||||
if (sd == -1) {
|
||||
oclog(ws, LOG_ERR,
|
||||
"failed connecting to sec mod");
|
||||
goto auth_fail;
|
||||
}
|
||||
|
||||
ret =
|
||||
send_msg_to_secmod(ws, sd,
|
||||
SM_CMD_AUTH_CONT,
|
||||
&rreq,
|
||||
(pack_size_func)
|
||||
sec_auth_cont_msg__get_packed_size,
|
||||
(pack_func)
|
||||
sec_auth_cont_msg__pack);
|
||||
talloc_free(username);
|
||||
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_ERR,
|
||||
"failed sending auth reinit message to main");
|
||||
goto auth_fail;
|
||||
}
|
||||
|
||||
ws->auth_state = S_AUTH_INIT;
|
||||
goto recv_reply;
|
||||
}
|
||||
oclog(ws, LOG_DEBUG, "failed reading groupname");
|
||||
} else {
|
||||
snprintf(ws->groupname, sizeof(ws->groupname), "%s",
|
||||
groupname);
|
||||
ireq.group_name = ws->groupname;
|
||||
talloc_free(groupname);
|
||||
}
|
||||
|
||||
ret = parse_reply(ws, req->body, req->body_length,
|
||||
USERNAME_FIELD, sizeof(USERNAME_FIELD)-1,
|
||||
NULL, 0,
|
||||
&username);
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_INFO, "failed reading username");
|
||||
goto ask_auth;
|
||||
}
|
||||
@@ -952,9 +928,10 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
|
||||
SecAuthContMsg areq = SEC_AUTH_CONT_MSG__INIT;
|
||||
|
||||
if (ws->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
|
||||
ret =
|
||||
read_user_pass(ws, req->body, req->body_length,
|
||||
NULL, &password);
|
||||
ret = parse_reply(ws, req->body, req->body_length,
|
||||
PASSWORD_FIELD, sizeof(PASSWORD_FIELD)-1,
|
||||
NULL, 0,
|
||||
&password);
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_ERR, "failed reading password");
|
||||
goto auth_fail;
|
||||
@@ -996,7 +973,6 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
|
||||
goto auth_fail;
|
||||
}
|
||||
|
||||
recv_reply:
|
||||
ret = recv_auth_reply(ws, sd, msg, sizeof(msg));
|
||||
if (sd != -1)
|
||||
close(sd);
|
||||
@@ -1019,11 +995,13 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
|
||||
return post_common_handler(ws, http_ver);
|
||||
|
||||
ask_auth:
|
||||
|
||||
if (sd != -1)
|
||||
close(sd);
|
||||
return get_auth_handler(ws, http_ver);
|
||||
|
||||
auth_fail:
|
||||
|
||||
if (sd != -1)
|
||||
close(sd);
|
||||
tls_printf(ws->session,
|
||||
|
||||
@@ -197,6 +197,7 @@ typedef struct worker_st {
|
||||
|
||||
|
||||
char username[MAX_USERNAME_SIZE];
|
||||
char groupname[MAX_USERNAME_SIZE];
|
||||
char hostname[MAX_HOSTNAME_SIZE];
|
||||
uint8_t cookie[COOKIE_SIZE];
|
||||
unsigned int cookie_set;
|
||||
|
||||
Reference in New Issue
Block a user