mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-02-10 00:37:00 +08:00
Allow multiple groups to be present in a client certificate.
In that case the user will be prompted to select a group.
This commit is contained in:
@@ -270,6 +270,7 @@ struct passwd * pwd;
|
||||
struct pam_ctx_st * pctx = ctx;
|
||||
struct group *grp;
|
||||
int ret;
|
||||
unsigned found;
|
||||
|
||||
groupname[0] = 0;
|
||||
|
||||
@@ -285,14 +286,22 @@ int ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
found = 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;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found == 0) {
|
||||
syslog(LOG_AUTH,
|
||||
"user '%s' requested group '%s' but he is not a member",
|
||||
pctx->username, suggested);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
struct group* grp = getgrgid(pwd->pw_gid);
|
||||
if (grp != NULL)
|
||||
|
||||
@@ -89,6 +89,14 @@ break_group_list(void *pool, char *text,
|
||||
/* skip the group */
|
||||
(*elements)--;
|
||||
}
|
||||
} else {
|
||||
p2 = strrchr(broken_text[(*elements)-1], ' ');
|
||||
if (p2 != NULL) {
|
||||
while (c_isspace(*p2)) {
|
||||
*p2 = 0;
|
||||
p2--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (p != NULL && *elements < MAX_GROUPS);
|
||||
@@ -182,7 +190,7 @@ static int plain_auth_init(void **ctx, void *pool, const char *username, const c
|
||||
static int plain_auth_group(void *ctx, const char *suggested, char *groupname, int groupname_size)
|
||||
{
|
||||
struct plain_ctx_st *pctx = ctx;
|
||||
unsigned i;
|
||||
unsigned i, found = 0;
|
||||
|
||||
groupname[0] = 0;
|
||||
|
||||
@@ -190,9 +198,17 @@ static int plain_auth_group(void *ctx, const char *suggested, char *groupname, i
|
||||
for (i=0;i<pctx->groupnames_size;i++) {
|
||||
if (strcmp(suggested, pctx->groupnames[i]) == 0) {
|
||||
snprintf(groupname, groupname_size, "%s", pctx->groupnames[i]);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found == 0) {
|
||||
syslog(LOG_AUTH,
|
||||
"user '%s' requested group '%s' but he is not a member",
|
||||
pctx->username, suggested);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (pctx->groupnames_size > 0 && groupname[0] == 0) {
|
||||
|
||||
@@ -18,8 +18,6 @@ message auth_cookie_request_msg
|
||||
{
|
||||
required bytes cookie = 1;
|
||||
required bool tls_auth_ok = 2 [default = false];
|
||||
optional string cert_user_name = 3;
|
||||
optional string cert_group_name = 4;
|
||||
}
|
||||
|
||||
/* AUTH_REP */
|
||||
@@ -131,7 +129,7 @@ message sec_auth_init_msg
|
||||
required string user_name = 3;
|
||||
optional string group_name = 4; /* selected group name */
|
||||
optional string cert_user_name = 5;
|
||||
optional string cert_group_name = 6;
|
||||
repeated string cert_group_names = 6;
|
||||
optional string hostname = 7;
|
||||
required string ip = 8;
|
||||
}
|
||||
|
||||
@@ -188,8 +188,11 @@ int send_sec_auth_reply_msg(sec_mod_st * sec, client_entry_st * e)
|
||||
|
||||
static int check_user_group_status(sec_mod_st * sec, client_entry_st * e,
|
||||
int tls_auth_ok, const char *cert_user,
|
||||
const char *cert_group)
|
||||
char **cert_groups,
|
||||
unsigned cert_groups_size)
|
||||
{
|
||||
unsigned found, i;
|
||||
|
||||
if (sec->config->auth_types & AUTH_TYPE_CERTIFICATE) {
|
||||
if (tls_auth_ok == 0 && sec->config->cisco_client_compat == 0) {
|
||||
seclog(LOG_INFO, "user '%s' presented no certificate",
|
||||
@@ -198,26 +201,39 @@ static int check_user_group_status(sec_mod_st * sec, client_entry_st * e,
|
||||
}
|
||||
|
||||
if (tls_auth_ok != 0) {
|
||||
if (e->username[0] == 0) {
|
||||
if (e->username[0] == 0 && sec->config->cert_user_oid != NULL) {
|
||||
if (cert_user == NULL) {
|
||||
seclog(LOG_INFO, "no username in the certificate!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(e->username, sizeof(e->username), "%s",
|
||||
cert_user);
|
||||
snprintf(e->groupname, sizeof(e->groupname),
|
||||
"%s", cert_group);
|
||||
if (cert_groups_size > 0 && sec->config->cert_group_oid != NULL)
|
||||
snprintf(e->groupname, sizeof(e->groupname),
|
||||
"%s", cert_groups[0]);
|
||||
} else {
|
||||
if (strcmp(e->username, cert_user) != 0) {
|
||||
if (sec->config->cert_user_oid != NULL && cert_user && strcmp(e->username, cert_user) != 0) {
|
||||
seclog(LOG_INFO,
|
||||
"user '%s' presented a certificate from user '%s'",
|
||||
e->username, cert_user);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sec->config->cert_group_oid != NULL
|
||||
&& strcmp(e->groupname, cert_group) != 0) {
|
||||
seclog(LOG_INFO,
|
||||
"user '%s' presented a certificate from group '%s' but he is member of '%s'",
|
||||
e->username, cert_group,
|
||||
e->groupname);
|
||||
return -1;
|
||||
if (sec->config->cert_group_oid != NULL) {
|
||||
found = 0;
|
||||
for (i=0;i<cert_groups_size;i++) {
|
||||
if (strcmp(e->groupname, cert_groups[i]) == 0) {
|
||||
found++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found == 0) {
|
||||
seclog(LOG_INFO,
|
||||
"user '%s' presented a certificate from group '%s' but he isn't a member of it",
|
||||
e->username, e->groupname);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -379,10 +395,30 @@ int handle_sec_auth_init(sec_mod_st * sec, const SecAuthInitMsg * req)
|
||||
}
|
||||
}
|
||||
|
||||
if (sec->config->auth_types & AUTH_TYPE_CERTIFICATE) {
|
||||
if (e->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) {
|
||||
snprintf(e->groupname, sizeof(e->groupname), "%s", req->cert_group_names[i]);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found == 0) {
|
||||
seclog(LOG_AUTH, "user '%s' requested group '%s' but it is not part of his certificate groups",
|
||||
req->user_name, req->group_name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret =
|
||||
check_user_group_status(sec, e, req->tls_auth_ok,
|
||||
req->cert_user_name, req->cert_group_name);
|
||||
req->cert_user_name, req->cert_group_names,
|
||||
req->n_cert_group_names);
|
||||
if (ret < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
@@ -79,12 +79,14 @@ static const char login_msg_no_user_end[] =
|
||||
"<input type=\"password\" name=\"password\" label=\"Password:\" />\n"
|
||||
"</form></auth></config-auth>\n";
|
||||
|
||||
static int get_cert_info(worker_st * ws);
|
||||
|
||||
int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg)
|
||||
{
|
||||
int ret;
|
||||
char context[BASE64_LENGTH(SID_SIZE) + 1];
|
||||
char temp[128];
|
||||
unsigned int i;
|
||||
unsigned int i, j;
|
||||
str_st str;
|
||||
|
||||
str_init(&str, ws);
|
||||
@@ -142,15 +144,24 @@ int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg)
|
||||
}
|
||||
|
||||
} else {
|
||||
/* ask for username and group */
|
||||
/* ask for username and groups */
|
||||
ret = str_append_str(&str, login_msg_user_start);
|
||||
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->groupname[0] == 0 && ws->config->group_list_size > 0) {
|
||||
if (ws->groupname[0] == 0 && (ws->config->group_list_size > 0 || ws->config->group_list_size > 0)) {
|
||||
ret = str_append_str(&str, "<select name=\"group_list\" label=\"GROUP:\">\n");
|
||||
if (ret < 0) {
|
||||
ret = -1;
|
||||
@@ -166,6 +177,30 @@ int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg)
|
||||
}
|
||||
}
|
||||
|
||||
/* append any groups available in the certificate */
|
||||
if (ws->config->auth_types & 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)
|
||||
continue;
|
||||
snprintf(temp, sizeof(temp), "<option>%s</option>\n", ws->cert_groups[i]);
|
||||
ret = str_append_str(&str, temp);
|
||||
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);
|
||||
@@ -235,12 +270,15 @@ int get_auth_handler(worker_st * ws, unsigned http_ver)
|
||||
}
|
||||
|
||||
static
|
||||
int get_cert_names(worker_st * ws, const gnutls_datum_t * raw,
|
||||
char *username, size_t username_size,
|
||||
char *groupname, size_t groupname_size)
|
||||
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) {
|
||||
@@ -256,13 +294,14 @@ int get_cert_names(worker_st * ws, const gnutls_datum_t * raw,
|
||||
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, username, &username_size);
|
||||
ws->config->cert_user_oid, 0,
|
||||
0, ws->cert_username, &size);
|
||||
} else {
|
||||
ret = gnutls_x509_crt_get_dn(crt, username, &username_size);
|
||||
ret = gnutls_x509_crt_get_dn(crt, ws->cert_username, &size);
|
||||
}
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_ERR, "cannot obtain user from certificate DN: %s",
|
||||
@@ -271,19 +310,53 @@ int get_cert_names(worker_st * ws, const gnutls_datum_t * raw,
|
||||
}
|
||||
|
||||
if (ws->config->cert_group_oid) {
|
||||
ret =
|
||||
gnutls_x509_crt_get_dn_by_oid(crt,
|
||||
ws->config->cert_group_oid, 0,
|
||||
0, groupname,
|
||||
&groupname_size);
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_ERR,
|
||||
"cannot obtain group from certificate DN: %s",
|
||||
gnutls_strerror(ret));
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
groupname[0] = 0;
|
||||
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;
|
||||
@@ -574,8 +647,7 @@ static int recv_auth_reply(worker_st * ws, int sd, char *txt,
|
||||
|
||||
/* grabs the username from the session certificate */
|
||||
static
|
||||
int get_cert_info(worker_st * ws, char *user, unsigned user_size,
|
||||
char *group, unsigned group_size)
|
||||
int get_cert_info(worker_st * ws)
|
||||
{
|
||||
const gnutls_datum_t *cert;
|
||||
unsigned int ncerts;
|
||||
@@ -589,7 +661,7 @@ int get_cert_info(worker_st * ws, char *user, unsigned user_size,
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = get_cert_names(ws, cert, user, user_size, group, group_size);
|
||||
ret = get_cert_names(ws, cert);
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_ERR, "cannot get username (%s) from certificate",
|
||||
ws->config->cert_user_oid);
|
||||
@@ -607,8 +679,6 @@ int auth_cookie(worker_st * ws, void *cookie, size_t cookie_size)
|
||||
{
|
||||
int ret;
|
||||
AuthCookieRequestMsg msg = AUTH_COOKIE_REQUEST_MSG__INIT;
|
||||
char tmp_user[MAX_USERNAME_SIZE];
|
||||
char tmp_group[MAX_USERNAME_SIZE];
|
||||
|
||||
if ((ws->config->auth_types & AUTH_TYPE_CERTIFICATE)
|
||||
&& ws->config->cisco_client_compat == 0) {
|
||||
@@ -618,16 +688,13 @@ int auth_cookie(worker_st * ws, void *cookie, size_t cookie_size)
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = get_cert_info(ws, tmp_user, sizeof(tmp_user),
|
||||
tmp_group, sizeof(tmp_group));
|
||||
ret = get_cert_info(ws);
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_INFO, "cannot obtain certificate info");
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg.tls_auth_ok = 1;
|
||||
msg.cert_user_name = tmp_user;
|
||||
msg.cert_group_name = tmp_group;
|
||||
}
|
||||
|
||||
msg.cookie.data = cookie;
|
||||
@@ -847,8 +914,6 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
|
||||
char *username = NULL;
|
||||
char *password = NULL;
|
||||
char *groupname = NULL;
|
||||
char tmp_user[MAX_USERNAME_SIZE];
|
||||
char tmp_group[MAX_USERNAME_SIZE];
|
||||
char ipbuf[128];
|
||||
char msg[MAX_MSG_SIZE];
|
||||
|
||||
@@ -898,8 +963,7 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
|
||||
goto auth_fail;
|
||||
}
|
||||
|
||||
ret = get_cert_info(ws, tmp_user, sizeof(tmp_user),
|
||||
tmp_group, sizeof(tmp_group));
|
||||
ret = get_cert_info(ws);
|
||||
if (ret < 0) {
|
||||
oclog(ws, LOG_ERR,
|
||||
"failed reading certificate info");
|
||||
@@ -907,8 +971,9 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
|
||||
}
|
||||
|
||||
ireq.tls_auth_ok = 1;
|
||||
ireq.cert_user_name = tmp_user;
|
||||
ireq.cert_group_name = tmp_group;
|
||||
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;
|
||||
|
||||
@@ -198,6 +198,11 @@ typedef struct worker_st {
|
||||
|
||||
char username[MAX_USERNAME_SIZE];
|
||||
char groupname[MAX_USERNAME_SIZE];
|
||||
|
||||
char cert_username[MAX_USERNAME_SIZE];
|
||||
char **cert_groups;
|
||||
unsigned cert_groups_size;
|
||||
|
||||
char hostname[MAX_HOSTNAME_SIZE];
|
||||
uint8_t cookie[COOKIE_SIZE];
|
||||
unsigned int cookie_set;
|
||||
|
||||
Reference in New Issue
Block a user