@@ -396,6 +396,12 @@ static const PQEnvironmentOption EnvironmentOptions[] =
396
396
}
397
397
};
398
398
399
+ static const pg_fe_sasl_mech * supported_sasl_mechs [] =
400
+ {
401
+ & pg_scram_mech ,
402
+ };
403
+ #define SASL_MECHANISM_COUNT lengthof(supported_sasl_mechs)
404
+
399
405
/* The connection URI must start with either of the following designators: */
400
406
static const char uri_designator [] = "postgresql://" ;
401
407
static const char short_uri_designator [] = "postgres://" ;
@@ -1117,6 +1123,57 @@ libpq_prng_init(PGconn *conn)
1117
1123
pg_prng_seed (& conn -> prng_state , rseed );
1118
1124
}
1119
1125
1126
+ /*
1127
+ * Fills the connection's allowed_sasl_mechs list with all supported SASL
1128
+ * mechanisms.
1129
+ */
1130
+ static inline void
1131
+ fill_allowed_sasl_mechs (PGconn * conn )
1132
+ {
1133
+ /*---
1134
+ * We only support one mechanism at the moment, so rather than deal with a
1135
+ * linked list, conn->allowed_sasl_mechs is an array of static length. We
1136
+ * rely on the compile-time assertion here to keep us honest.
1137
+ *
1138
+ * To add a new mechanism to require_auth,
1139
+ * - add it to supported_sasl_mechs,
1140
+ * - update the length of conn->allowed_sasl_mechs,
1141
+ * - handle the new mechanism name in the require_auth portion of
1142
+ * pqConnectOptions2(), below.
1143
+ */
1144
+ StaticAssertDecl (lengthof (conn -> allowed_sasl_mechs ) == SASL_MECHANISM_COUNT ,
1145
+ "conn->allowed_sasl_mechs[] is not sufficiently large for holding all supported SASL mechanisms" );
1146
+
1147
+ for (int i = 0 ; i < SASL_MECHANISM_COUNT ; i ++ )
1148
+ conn -> allowed_sasl_mechs [i ] = supported_sasl_mechs [i ];
1149
+ }
1150
+
1151
+ /*
1152
+ * Clears the connection's allowed_sasl_mechs list.
1153
+ */
1154
+ static inline void
1155
+ clear_allowed_sasl_mechs (PGconn * conn )
1156
+ {
1157
+ for (int i = 0 ; i < lengthof (conn -> allowed_sasl_mechs ); i ++ )
1158
+ conn -> allowed_sasl_mechs [i ] = NULL ;
1159
+ }
1160
+
1161
+ /*
1162
+ * Helper routine that searches the static allowed_sasl_mechs list for a
1163
+ * specific mechanism.
1164
+ */
1165
+ static inline int
1166
+ index_of_allowed_sasl_mech (PGconn * conn , const pg_fe_sasl_mech * mech )
1167
+ {
1168
+ for (int i = 0 ; i < lengthof (conn -> allowed_sasl_mechs ); i ++ )
1169
+ {
1170
+ if (conn -> allowed_sasl_mechs [i ] == mech )
1171
+ return i ;
1172
+ }
1173
+
1174
+ return -1 ;
1175
+ }
1176
+
1120
1177
/*
1121
1178
* pqConnectOptions2
1122
1179
*
@@ -1358,17 +1415,19 @@ pqConnectOptions2(PGconn *conn)
1358
1415
bool negated = false;
1359
1416
1360
1417
/*
1361
- * By default, start from an empty set of allowed options and add to
1362
- * it.
1418
+ * By default, start from an empty set of allowed methods and
1419
+ * mechanisms, and add to it.
1363
1420
*/
1364
1421
conn -> auth_required = true;
1365
1422
conn -> allowed_auth_methods = 0 ;
1423
+ clear_allowed_sasl_mechs (conn );
1366
1424
1367
1425
for (first = true, more = true; more ; first = false)
1368
1426
{
1369
1427
char * method ,
1370
1428
* part ;
1371
- uint32 bits ;
1429
+ uint32 bits = 0 ;
1430
+ const pg_fe_sasl_mech * mech = NULL ;
1372
1431
1373
1432
part = parse_comma_separated_list (& s , & more );
1374
1433
if (part == NULL )
@@ -1384,11 +1443,12 @@ pqConnectOptions2(PGconn *conn)
1384
1443
if (first )
1385
1444
{
1386
1445
/*
1387
- * Switch to a permissive set of allowed options, and
1388
- * subtract from it.
1446
+ * Switch to a permissive set of allowed methods and
1447
+ * mechanisms, and subtract from it.
1389
1448
*/
1390
1449
conn -> auth_required = false;
1391
1450
conn -> allowed_auth_methods = -1 ;
1451
+ fill_allowed_sasl_mechs (conn );
1392
1452
}
1393
1453
else if (!negated )
1394
1454
{
@@ -1413,6 +1473,10 @@ pqConnectOptions2(PGconn *conn)
1413
1473
return false;
1414
1474
}
1415
1475
1476
+ /*
1477
+ * First group: methods that can be handled solely with the
1478
+ * authentication request codes.
1479
+ */
1416
1480
if (strcmp (method , "password" ) == 0 )
1417
1481
{
1418
1482
bits = (1 << AUTH_REQ_PASSWORD );
@@ -1431,13 +1495,21 @@ pqConnectOptions2(PGconn *conn)
1431
1495
bits = (1 << AUTH_REQ_SSPI );
1432
1496
bits |= (1 << AUTH_REQ_GSS_CONT );
1433
1497
}
1498
+
1499
+ /*
1500
+ * Next group: SASL mechanisms. All of these use the same request
1501
+ * codes, so the list of allowed mechanisms is tracked separately.
1502
+ *
1503
+ * supported_sasl_mechs must contain all mechanisms handled here.
1504
+ */
1434
1505
else if (strcmp (method , "scram-sha-256" ) == 0 )
1435
1506
{
1436
- /* This currently assumes that SCRAM is the only SASL method. */
1437
- bits = (1 << AUTH_REQ_SASL );
1438
- bits |= (1 << AUTH_REQ_SASL_CONT );
1439
- bits |= (1 << AUTH_REQ_SASL_FIN );
1507
+ mech = & pg_scram_mech ;
1440
1508
}
1509
+
1510
+ /*
1511
+ * Final group: meta-options.
1512
+ */
1441
1513
else if (strcmp (method , "none" ) == 0 )
1442
1514
{
1443
1515
/*
@@ -1473,20 +1545,68 @@ pqConnectOptions2(PGconn *conn)
1473
1545
return false;
1474
1546
}
1475
1547
1476
- /* Update the bitmask. */
1477
- if (negated )
1548
+ if (mech )
1478
1549
{
1479
- if ((conn -> allowed_auth_methods & bits ) == 0 )
1480
- goto duplicate ;
1550
+ /*
1551
+ * Update the mechanism set only. The method bitmask will be
1552
+ * updated for SASL further down.
1553
+ */
1554
+ Assert (!bits );
1555
+
1556
+ if (negated )
1557
+ {
1558
+ /* Remove the existing mechanism from the list. */
1559
+ i = index_of_allowed_sasl_mech (conn , mech );
1560
+ if (i < 0 )
1561
+ goto duplicate ;
1562
+
1563
+ conn -> allowed_sasl_mechs [i ] = NULL ;
1564
+ }
1565
+ else
1566
+ {
1567
+ /*
1568
+ * Find a space to put the new mechanism (after making
1569
+ * sure it's not already there).
1570
+ */
1571
+ i = index_of_allowed_sasl_mech (conn , mech );
1572
+ if (i >= 0 )
1573
+ goto duplicate ;
1481
1574
1482
- conn -> allowed_auth_methods &= ~bits ;
1575
+ i = index_of_allowed_sasl_mech (conn , NULL );
1576
+ if (i < 0 )
1577
+ {
1578
+ /* Should not happen; the pointer list is corrupted. */
1579
+ Assert (false);
1580
+
1581
+ conn -> status = CONNECTION_BAD ;
1582
+ libpq_append_conn_error (conn ,
1583
+ "internal error: no space in allowed_sasl_mechs" );
1584
+ free (part );
1585
+ return false;
1586
+ }
1587
+
1588
+ conn -> allowed_sasl_mechs [i ] = mech ;
1589
+ }
1483
1590
}
1484
1591
else
1485
1592
{
1486
- if ((conn -> allowed_auth_methods & bits ) == bits )
1487
- goto duplicate ;
1593
+ /* Update the method bitmask. */
1594
+ Assert (bits );
1595
+
1596
+ if (negated )
1597
+ {
1598
+ if ((conn -> allowed_auth_methods & bits ) == 0 )
1599
+ goto duplicate ;
1600
+
1601
+ conn -> allowed_auth_methods &= ~bits ;
1602
+ }
1603
+ else
1604
+ {
1605
+ if ((conn -> allowed_auth_methods & bits ) == bits )
1606
+ goto duplicate ;
1488
1607
1489
- conn -> allowed_auth_methods |= bits ;
1608
+ conn -> allowed_auth_methods |= bits ;
1609
+ }
1490
1610
}
1491
1611
1492
1612
free (part );
@@ -1505,6 +1625,36 @@ pqConnectOptions2(PGconn *conn)
1505
1625
free (part );
1506
1626
return false;
1507
1627
}
1628
+
1629
+ /*
1630
+ * Finally, allow SASL authentication requests if (and only if) we've
1631
+ * allowed any mechanisms.
1632
+ */
1633
+ {
1634
+ bool allowed = false;
1635
+ const uint32 sasl_bits =
1636
+ (1 << AUTH_REQ_SASL )
1637
+ | (1 << AUTH_REQ_SASL_CONT )
1638
+ | (1 << AUTH_REQ_SASL_FIN );
1639
+
1640
+ for (i = 0 ; i < lengthof (conn -> allowed_sasl_mechs ); i ++ )
1641
+ {
1642
+ if (conn -> allowed_sasl_mechs [i ])
1643
+ {
1644
+ allowed = true;
1645
+ break ;
1646
+ }
1647
+ }
1648
+
1649
+ /*
1650
+ * For the standard case, add the SASL bits to the (default-empty)
1651
+ * set if needed. For the negated case, remove them.
1652
+ */
1653
+ if (!negated && allowed )
1654
+ conn -> allowed_auth_methods |= sasl_bits ;
1655
+ else if (negated && !allowed )
1656
+ conn -> allowed_auth_methods &= ~sasl_bits ;
1657
+ }
1508
1658
}
1509
1659
1510
1660
/*
0 commit comments