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:
Nikos Mavrogiannopoulos
2014-05-20 13:51:05 +02:00
parent 2668fe63b4
commit aef5dc0633
6 changed files with 184 additions and 55 deletions

View File

@@ -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)

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;