Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 35e2e35

Browse files
committed
Add authentication parameters compat_realm and upn_usename for SSPI
These parameters are available for SSPI authentication only, to make it possible to make it behave more like "normal gssapi", while making it possible to maintain compatibility. compat_realm is on by default, but can be turned off to make the authentication use the full Kerberos realm instead of the NetBIOS name. upn_username is off by default, and can be turned on to return the users Kerberos UPN rather than the SAM-compatible name (a user in Active Directory can have both a legacy SAM-compatible username and a new Kerberos one. Normally they are the same, but not always) Author: Christian Ullrich Reviewed by: Robbie Harwood, Alvaro Herrera, me
1 parent cb0c8cb commit 35e2e35

File tree

4 files changed

+178
-0
lines changed

4 files changed

+178
-0
lines changed

doc/src/sgml/client-auth.sgml

+37
Original file line numberDiff line numberDiff line change
@@ -1106,6 +1106,43 @@ omicron bryanh guest1
11061106
</listitem>
11071107
</varlistentry>
11081108

1109+
<varlistentry>
1110+
<term><literal>compat_realm</literal></term>
1111+
<listitem>
1112+
<para>
1113+
If set to 1, the domain's SAM-compatible name (also known as the
1114+
NetBIOS name) is used for the <literal>include_realm</literal>
1115+
option. This is the default. If set to 0, the true realm name from
1116+
the Kerberos user principal name is used.
1117+
</para>
1118+
<para>
1119+
Do not enable this option unless your server runs under a domain
1120+
account (this includes virtual service accounts on a domain member
1121+
system) and all clients authenticating through SSPI are also using
1122+
domain accounts, or authentication will fail.
1123+
</para>
1124+
</listitem>
1125+
</varlistentry>
1126+
1127+
<varlistentry>
1128+
<term><literal>upn_username</literal></term>
1129+
<listitem>
1130+
<para>
1131+
If this option is enabled along with <literal>compat_realm</literal>,
1132+
the user name from the Kerberos UPN is used for authentication. If
1133+
it is disabled (the default), the SAM-compatible user name is used.
1134+
By default, these two names are identical for new user accounts.
1135+
</para>
1136+
<para>
1137+
Note that <application>libpq</> uses the SAM-compatible name if no
1138+
explicit user name is specified. If you use
1139+
<application>libpq</> or a driver based on it, you should
1140+
leave this option disabled or explicitly specify user name in the
1141+
connection string.
1142+
</para>
1143+
</listitem>
1144+
</varlistentry>
1145+
11091146
<varlistentry>
11101147
<term><literal>map</literal></term>
11111148
<listitem>

src/backend/libpq/auth.c

+110
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@ typedef SECURITY_STATUS
166166
(WINAPI * QUERY_SECURITY_CONTEXT_TOKEN_FN) (
167167
PCtxtHandle, void **);
168168
static int pg_SSPI_recvauth(Port *port);
169+
static int pg_SSPI_make_upn(char *accountname,
170+
size_t accountnamesize,
171+
char *domainname,
172+
size_t domainnamesize,
173+
bool update_accountname);
169174
#endif
170175

171176
/*----------------------------------------------------------------
@@ -1287,6 +1292,17 @@ pg_SSPI_recvauth(Port *port)
12871292

12881293
free(tokenuser);
12891294

1295+
if (!port->hba->compat_realm)
1296+
{
1297+
int status = pg_SSPI_make_upn(accountname, sizeof(accountname),
1298+
domainname, sizeof(domainname),
1299+
port->hba->upn_username);
1300+
1301+
if (status != STATUS_OK)
1302+
/* Error already reported from pg_SSPI_make_upn */
1303+
return status;
1304+
}
1305+
12901306
/*
12911307
* Compare realm/domain if requested. In SSPI, always compare case
12921308
* insensitive.
@@ -1322,6 +1338,100 @@ pg_SSPI_recvauth(Port *port)
13221338
else
13231339
return check_usermap(port->hba->usermap, port->user_name, accountname, true);
13241340
}
1341+
1342+
/*
1343+
* Replaces the domainname with the Kerberos realm name,
1344+
* and optionally the accountname with the Kerberos user name.
1345+
*/
1346+
static int
1347+
pg_SSPI_make_upn(char *accountname,
1348+
size_t accountnamesize,
1349+
char *domainname,
1350+
size_t domainnamesize,
1351+
bool update_accountname)
1352+
{
1353+
char *samname;
1354+
char *upname = NULL;
1355+
char *p = NULL;
1356+
ULONG upnamesize = 0;
1357+
size_t upnamerealmsize;
1358+
BOOLEAN res;
1359+
1360+
/*
1361+
* Build SAM name (DOMAIN\user), then translate to UPN
1362+
* (user@kerberos.realm). The realm name is returned in lower case, but
1363+
* that is fine because in SSPI auth, string comparisons are always
1364+
* case-insensitive.
1365+
*/
1366+
1367+
samname = psprintf("%s\\%s", domainname, accountname);
1368+
res = TranslateName(samname, NameSamCompatible, NameUserPrincipal,
1369+
NULL, &upnamesize);
1370+
1371+
if ((!res && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1372+
|| upnamesize == 0)
1373+
{
1374+
pfree(samname);
1375+
ereport(LOG,
1376+
(errcode(ERRCODE_INVALID_ROLE_SPECIFICATION),
1377+
errmsg("could not translate name")));
1378+
return STATUS_ERROR;
1379+
}
1380+
1381+
/* upnamesize includes the terminating NUL. */
1382+
upname = palloc(upnamesize);
1383+
1384+
res = TranslateName(samname, NameSamCompatible, NameUserPrincipal,
1385+
upname, &upnamesize);
1386+
1387+
pfree(samname);
1388+
if (res)
1389+
p = strchr(upname, '@');
1390+
1391+
if (!res || p == NULL)
1392+
{
1393+
pfree(upname);
1394+
ereport(LOG,
1395+
(errcode(ERRCODE_INVALID_ROLE_SPECIFICATION),
1396+
errmsg("could not translate name")));
1397+
return STATUS_ERROR;
1398+
}
1399+
1400+
/* Length of realm name after the '@', including the NUL. */
1401+
upnamerealmsize = upnamesize - (p - upname + 1);
1402+
1403+
/* Replace domainname with realm name. */
1404+
if (upnamerealmsize > domainnamesize)
1405+
{
1406+
pfree(upname);
1407+
ereport(LOG,
1408+
(errcode(ERRCODE_INVALID_ROLE_SPECIFICATION),
1409+
errmsg("realm name too long")));
1410+
return STATUS_ERROR;
1411+
}
1412+
1413+
/* Length is now safe. */
1414+
strcpy(domainname, p + 1);
1415+
1416+
/* Replace account name as well (in case UPN != SAM)? */
1417+
if (update_accountname)
1418+
{
1419+
if ((p - upname + 1) > accountnamesize)
1420+
{
1421+
pfree(upname);
1422+
ereport(LOG,
1423+
(errcode(ERRCODE_INVALID_ROLE_SPECIFICATION),
1424+
errmsg("translated account name too long")));
1425+
return STATUS_ERROR;
1426+
}
1427+
1428+
*p = 0;
1429+
strcpy(accountname, upname);
1430+
}
1431+
1432+
pfree(upname);
1433+
return STATUS_OK;
1434+
}
13251435
#endif /* ENABLE_SSPI */
13261436

13271437

src/backend/libpq/hba.c

+29
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,17 @@ parse_hba_line(List *line, int line_num, char *raw_line)
12931293
parsedline->auth_method == uaSSPI)
12941294
parsedline->include_realm = true;
12951295

1296+
/*
1297+
* For SSPI, include_realm defaults to the SAM-compatible domain (aka
1298+
* NetBIOS name) and user names instead of the Kerberos principal name for
1299+
* compatibility.
1300+
*/
1301+
if (parsedline->auth_method == uaSSPI)
1302+
{
1303+
parsedline->compat_realm = true;
1304+
parsedline->upn_username = false;
1305+
}
1306+
12961307
/* Parse remaining arguments */
12971308
while ((field = lnext(field)) != NULL)
12981309
{
@@ -1585,6 +1596,24 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
15851596
else
15861597
hbaline->include_realm = false;
15871598
}
1599+
else if (strcmp(name, "compat_realm") == 0)
1600+
{
1601+
if (hbaline->auth_method != uaSSPI)
1602+
INVALID_AUTH_OPTION("compat_realm", gettext_noop("sspi"));
1603+
if (strcmp(val, "1") == 0)
1604+
hbaline->compat_realm = true;
1605+
else
1606+
hbaline->compat_realm = false;
1607+
}
1608+
else if (strcmp(name, "upn_username") == 0)
1609+
{
1610+
if (hbaline->auth_method != uaSSPI)
1611+
INVALID_AUTH_OPTION("upn_username", gettext_noop("sspi"));
1612+
if (strcmp(val, "1") == 0)
1613+
hbaline->upn_username = true;
1614+
else
1615+
hbaline->upn_username = false;
1616+
}
15881617
else if (strcmp(name, "radiusserver") == 0)
15891618
{
15901619
struct addrinfo *gai_result;

src/include/libpq/hba.h

+2
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ typedef struct HbaLine
7979
bool clientcert;
8080
char *krb_realm;
8181
bool include_realm;
82+
bool compat_realm;
83+
bool upn_username;
8284
char *radiusserver;
8385
char *radiussecret;
8486
char *radiusidentifier;

0 commit comments

Comments
 (0)