Hi all, this patch adds the possibility to map the login-rolename to a different rolename actually used for permissions.
What is it used for? I'm working with smartcard based TLS-authentication to connect to the PG server. Authentication is done with the keys and certificates from the card within the TLS handshake. Certificate-CN and login-username have to be the same or have to match by the "pg_ident.conf". The role actually used for permissions is always the login-username. This patch allowes, to change the actually permissions to a role based on the certificate-CN. It is realised by an additional column in "pg_ident.conf". When using ODBC, you have to setup a fixed username which is used for login. Different permissions depending on the CN of the certificate on the current smartcard could be achieved by the following line: # MAPNAME SYSTEM-USERNAME PG-USERNAME EFFECTIVE-USERNAME ssl-user /(.*) dummy \1 The extension could be similar used for kerberos authentication, too. Bytheway I refactored the pg_ident-code a little bit, to avoid duplicated code and to allow substitution of more than one match (\2, \3 etc). Questions (I'm quite new to the PG-sources and used to write Ruby code): - Is this something useful - or is there a much easier way? - Are there any implementation shortcomings? regards Lars Kanis
diff -ur postgresql-8.4rc1.orig/src/backend/libpq/auth.c postgresql-8.4rc1/src/backend/libpq/auth.c
--- postgresql-8.4rc1.orig/src/backend/libpq/auth.c 2009-06-11 16:48:57.000000000 +0200
+++ postgresql-8.4rc1/src/backend/libpq/auth.c 2009-06-29 14:02:40.000000000 +0200
@@ -777,7 +777,7 @@
}
ret = check_usermap(port->hba->usermap, port->user_name, kusername,
- pg_krb_caseins_users);
+ pg_krb_caseins_users, &port->user_name);
krb5_free_ticket(pg_krb5_context, ticket);
krb5_auth_con_free(pg_krb5_context, auth_context);
@@ -1069,7 +1069,7 @@
}
ret = check_usermap(port->hba->usermap, port->user_name, gbuf.value,
- pg_krb_caseins_users);
+ pg_krb_caseins_users, &port->user_name);
gss_release_buffer(&lmin_s, &gbuf);
@@ -1360,12 +1360,12 @@
namebuf = palloc(strlen(accountname) + strlen(domainname) + 2);
sprintf(namebuf, "%...@%s", accountname, domainname);
- retval = check_usermap(port->hba->usermap, port->user_name, namebuf, true);
+ retval = check_usermap(port->hba->usermap, port->user_name, namebuf, true, &port->user_name);
pfree(namebuf);
return retval;
}
else
- return check_usermap(port->hba->usermap, port->user_name, accountname, true);
+ return check_usermap(port->hba->usermap, port->user_name, accountname, true, &port->user_name);
}
#endif /* ENABLE_SSPI */
@@ -1847,7 +1847,7 @@
return STATUS_ERROR;
}
- return check_usermap(port->hba->usermap, port->user_name, ident_user, false);
+ return check_usermap(port->hba->usermap, port->user_name, ident_user, false, &port->user_name);
}
@@ -2184,9 +2184,9 @@
port->user_name)));
return STATUS_ERROR;
}
-
+
/* Just pass the certificate CN to the usermap check */
- return check_usermap(port->hba->usermap, port->user_name, port->peer_cn, false);
+ return check_usermap(port->hba->usermap, port->user_name, port->peer_cn, false, &port->user_name);
}
#endif
diff -ur postgresql-8.4rc1.orig/src/backend/libpq/hba.c postgresql-8.4rc1/src/backend/libpq/hba.c
--- postgresql-8.4rc1.orig/src/backend/libpq/hba.c 2009-06-11 16:48:58.000000000 +0200
+++ postgresql-8.4rc1/src/backend/libpq/hba.c 2009-06-29 15:08:08.000000000 +0200
@@ -31,6 +31,7 @@
#include "storage/fd.h"
#include "utils/flatfiles.h"
#include "utils/guc.h"
+#include "utils/memutils.h"
@@ -1418,6 +1419,68 @@
return true;
}
+/* case (in-)sensitive string compare */
+static int strcmp_with_case( const char *str1, const char *str2, bool case_insensitive ){
+ if (case_insensitive)
+ {
+ return pg_strcasecmp(str1, str2);
+ }
+ else
+ {
+ return strcmp(str1, str2);
+ }
+}
+
+/*
+ Substitudes "\1", "\2", etc. within subst_in_str, based on the regexp-matches in extract_from_str.
+
+ returns substituded string. It has to be pfree'd.
+*/
+static char *regexp_substitude(size_t nr_matches, regmatch_t *matches, const char *extract_from_str, const char *subst_in_str){
+ char *ofs;
+ char *psubst_out_str;
+ int nr_match;
+ char *psubst_in_str;
+
+ psubst_in_str = psubst_out_str = pstrdup(subst_in_str);
+
+ for(nr_match = 1; nr_match <= nr_matches; nr_match++){
+ char subst_marker[5];
+
+ snprintf(subst_marker, sizeof(subst_marker), "\\%d", nr_match);
+
+ if ((ofs = strstr(psubst_in_str, subst_marker)) != NULL)
+ {
+ /* substitution of the first argument requested */
+ if (matches[nr_match].rm_so < 0)
+ {
+ pfree(psubst_in_str);
+ return NULL;
+ }
+
+ /*
+ * length: original length minus length of \1 plus length of match
+ * plus null terminator
+ */
+ psubst_out_str = palloc0(strlen(psubst_in_str) - 2 + (matches[nr_match].rm_eo - matches[nr_match].rm_so) + 1);
+ strncpy(psubst_out_str, psubst_in_str, (ofs - psubst_in_str));
+ memcpy(psubst_out_str + strlen(psubst_out_str),
+ extract_from_str + matches[nr_match].rm_so,
+ matches[nr_match].rm_eo - matches[nr_match].rm_so);
+ strcat(psubst_out_str, ofs + 2);
+ }
+ else
+ {
+ /* no substitution, so copy the match */
+ psubst_out_str = pstrdup(psubst_in_str);
+ }
+ pfree(psubst_in_str);
+ psubst_in_str = psubst_out_str;
+ }
+ return psubst_out_str;
+}
+
+
/*
* Process one line from the ident config file.
*
@@ -1427,12 +1490,13 @@
static void
parse_ident_usermap(List *line, int line_number, const char *usermap_name,
const char *pg_role, const char *ident_user,
- bool case_insensitive, bool *found_p, bool *error_p)
+ bool case_insensitive, bool *found_p, bool *error_p, const char **effective_role)
{
ListCell *line_item;
char *token;
char *file_map;
char *file_pgrole;
+ char *file_effective_role = NULL;
char *file_ident_user;
*found_p = false;
@@ -1459,6 +1523,11 @@
token = lfirst(line_item);
file_pgrole = token;
+ /* Get the PG effective role token */
+ line_item = lnext(line_item);
+ if (line_item)
+ file_effective_role = lfirst(line_item);
+
if (strcmp(file_map, usermap_name) != 0)
/* Line does not match the map name we're looking for, so just abort */
return;
@@ -1474,10 +1543,9 @@
*/
int r;
regex_t re;
- regmatch_t matches[2];
+ regmatch_t matches[10];
pg_wchar *wstr;
int wlen;
- char *ofs;
char *regexp_pgrole;
wstr = palloc((strlen(file_ident_user + 1) + 1) * sizeof(pg_wchar));
@@ -1506,7 +1574,7 @@
wstr = palloc((strlen(ident_user) + 1) * sizeof(pg_wchar));
wlen = pg_mb2wchar_with_len(ident_user, wstr, strlen(ident_user));
- r = pg_regexec(&re, wstr, wlen, 0, NULL, 2, matches, 0);
+ r = pg_regexec(&re, wstr, wlen, 0, NULL, sizeof(matches)/sizeof(*matches), matches, 0);
if (r)
{
char errstr[100];
@@ -1527,30 +1595,18 @@
}
pfree(wstr);
- if ((ofs = strstr(file_pgrole, "\\1")) != NULL)
+ regexp_pgrole = regexp_substitude(sizeof(matches)/sizeof(*matches), matches, ident_user, file_pgrole);
+
+ if (!regexp_pgrole)
{
- /* substitution of the first argument requested */
- if (matches[1].rm_so < 0)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
- errmsg("regular expression \"%s\" has no subexpressions as requested by backreference in \"%s\"",
- file_ident_user + 1, file_pgrole)));
-
- /*
- * length: original length minus length of \1 plus length of match
- * plus null terminator
- */
- regexp_pgrole = palloc0(strlen(file_pgrole) - 2 + (matches[1].rm_eo - matches[1].rm_so) + 1);
- strncpy(regexp_pgrole, file_pgrole, (ofs - file_pgrole));
- memcpy(regexp_pgrole + strlen(regexp_pgrole),
- ident_user + matches[1].rm_so,
- matches[1].rm_eo - matches[1].rm_so);
- strcat(regexp_pgrole, ofs + 2);
- }
- else
- {
- /* no substitution, so copy the match */
- regexp_pgrole = pstrdup(file_pgrole);
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
+ errmsg("regular expression \"%s\" has no subexpressions as requested by backreference in \"%s\"",
+ file_ident_user + 1, file_pgrole)));
+
+ pg_regfree(&re);
+ *error_p = true;
+ return;
}
pg_regfree(&re);
@@ -1559,15 +1615,28 @@
* now check if the username actually matched what the user is trying
* to connect as
*/
- if (case_insensitive)
- {
- if (pg_strcasecmp(regexp_pgrole, pg_role) == 0)
- *found_p = true;
- }
- else
+ if (strcmp_with_case(regexp_pgrole, pg_role, case_insensitive) == 0)
{
- if (strcmp(regexp_pgrole, pg_role) == 0)
- *found_p = true;
+ if (file_effective_role)
+ {
+ char *eff_role;
+
+ eff_role = regexp_substitude(sizeof(matches)/sizeof(*matches), matches, ident_user, file_effective_role);
+ if (!eff_role)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
+ errmsg("regular expression \"%s\" has no subexpressions as requested by backreference in \"%s\"",
+ file_ident_user + 1, file_effective_role)));
+
+ *error_p = true;
+ return;
+ }
+
+ *effective_role = MemoryContextStrdup(TopMemoryContext, eff_role);
+ }
+
+ *found_p = true;
}
pfree(regexp_pgrole);
@@ -1576,17 +1645,13 @@
else
{
/* Not regular expression, so make complete match */
- if (case_insensitive)
- {
- if (pg_strcasecmp(file_pgrole, pg_role) == 0 &&
- pg_strcasecmp(file_ident_user, ident_user) == 0)
- *found_p = true;
- }
- else
+ if (strcmp_with_case(file_pgrole, pg_role, case_insensitive) == 0 &&
+ strcmp_with_case(file_ident_user, ident_user, case_insensitive) == 0)
{
- if (strcmp(file_pgrole, pg_role) == 0 &&
- strcmp(file_ident_user, ident_user) == 0)
- *found_p = true;
+ if (file_effective_role)
+ *effective_role = MemoryContextStrdup(TopMemoryContext, file_effective_role);
+
+ *found_p = true;
}
}
@@ -1618,10 +1683,12 @@
check_usermap(const char *usermap_name,
const char *pg_role,
const char *auth_user,
- bool case_insensitive)
+ bool case_insensitive,
+ const char **effective_role)
{
bool found_entry = false,
- error = false;
+ error = false;
+ *effective_role = pg_role;
if (usermap_name == NULL || usermap_name[0] == '\0')
{
@@ -1649,7 +1716,7 @@
{
parse_ident_usermap(lfirst(line_cell), lfirst_int(num_cell),
usermap_name, pg_role, auth_user, case_insensitive,
- &found_entry, &error);
+ &found_entry, &error, effective_role);
if (found_entry || error)
break;
}
diff -ur postgresql-8.4rc1.orig/src/include/libpq/hba.h postgresql-8.4rc1/src/include/libpq/hba.h
--- postgresql-8.4rc1.orig/src/include/libpq/hba.h 2009-06-11 16:49:11.000000000 +0200
+++ postgresql-8.4rc1/src/include/libpq/hba.h 2009-06-25 13:24:41.000000000 +0200
@@ -72,7 +72,7 @@
Oid *dbtablespace, TransactionId *dbfrozenxid);
extern int check_usermap(const char *usermap_name,
const char *pg_role, const char *auth_user,
- bool case_sensitive);
+ bool case_sensitive, const char **effective_role);
extern bool pg_isblank(const char c);
#endif /* HBA_H */
signature.asc
Description: This is a digitally signed message part.
