|
10 | 10 | *
|
11 | 11 | *
|
12 | 12 | * IDENTIFICATION
|
13 |
| - * $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.175 2008/11/20 20:45:30 momjian Exp $ |
| 13 | + * $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.176 2008/11/28 14:26:58 mha Exp $ |
14 | 14 | *
|
15 | 15 | *-------------------------------------------------------------------------
|
16 | 16 | */
|
|
27 | 27 |
|
28 | 28 | #include "libpq/ip.h"
|
29 | 29 | #include "libpq/libpq.h"
|
| 30 | +#include "regex/regex.h" |
30 | 31 | #include "storage/fd.h"
|
31 | 32 | #include "utils/flatfiles.h"
|
32 | 33 | #include "utils/guc.h"
|
@@ -1403,20 +1404,128 @@ parse_ident_usermap(List *line, int line_number, const char *usermap_name,
|
1403 | 1404 | token = lfirst(line_item);
|
1404 | 1405 | file_pgrole = token;
|
1405 | 1406 |
|
| 1407 | + if (strcmp(file_map, usermap_name) != 0) |
| 1408 | + /* Line does not match the map name we're looking for, so just abort */ |
| 1409 | + return; |
| 1410 | + |
1406 | 1411 | /* Match? */
|
1407 |
| - if (case_insensitive) |
| 1412 | + if (file_ident_user[0] == '/') |
1408 | 1413 | {
|
1409 |
| - if (strcmp(file_map, usermap_name) == 0 && |
1410 |
| - pg_strcasecmp(file_pgrole, pg_role) == 0 && |
1411 |
| - pg_strcasecmp(file_ident_user, ident_user) == 0) |
1412 |
| - *found_p = true; |
| 1414 | + /* |
| 1415 | + * When system username starts with a slash, treat it as a regular expression. |
| 1416 | + * In this case, we process the system username as a regular expression that |
| 1417 | + * returns exactly one match. This is replaced for \1 in the database username |
| 1418 | + * string, if present. |
| 1419 | + */ |
| 1420 | + int r; |
| 1421 | + regex_t re; |
| 1422 | + regmatch_t matches[2]; |
| 1423 | + pg_wchar *wstr; |
| 1424 | + int wlen; |
| 1425 | + char *ofs; |
| 1426 | + char *regexp_pgrole; |
| 1427 | + |
| 1428 | + wstr = palloc((strlen(file_ident_user+1) + 1) * sizeof(pg_wchar)); |
| 1429 | + wlen = pg_mb2wchar_with_len(file_ident_user+1, wstr, strlen(file_ident_user+1)); |
| 1430 | + |
| 1431 | + /* |
| 1432 | + * XXX: Major room for optimization: regexps could be compiled when the file is loaded |
| 1433 | + * and then re-used in every connection. |
| 1434 | + */ |
| 1435 | + r = pg_regcomp(&re, wstr, wlen, REG_ADVANCED); |
| 1436 | + if (r) |
| 1437 | + { |
| 1438 | + char errstr[100]; |
| 1439 | + |
| 1440 | + pg_regerror(r, &re, errstr, sizeof(errstr)); |
| 1441 | + ereport(ERROR, |
| 1442 | + (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION), |
| 1443 | + errmsg("invalid regular expression '%s': %s", file_ident_user+1, errstr))); |
| 1444 | + |
| 1445 | + pfree(wstr); |
| 1446 | + *error_p = true; |
| 1447 | + return; |
| 1448 | + } |
| 1449 | + pfree(wstr); |
| 1450 | + |
| 1451 | + wstr = palloc((strlen(ident_user) + 1) * sizeof(pg_wchar)); |
| 1452 | + wlen = pg_mb2wchar_with_len(ident_user, wstr, strlen(ident_user)); |
| 1453 | + |
| 1454 | + r = pg_regexec(&re, wstr, wlen, 0, NULL, 2, matches,0); |
| 1455 | + if (r) |
| 1456 | + { |
| 1457 | + char errstr[100]; |
| 1458 | + |
| 1459 | + if (r != REG_NOMATCH) |
| 1460 | + { |
| 1461 | + /* REG_NOMATCH is not an error, everything else is */ |
| 1462 | + pg_regerror(r, &re, errstr, sizeof(errstr)); |
| 1463 | + ereport(ERROR, |
| 1464 | + (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION), |
| 1465 | + errmsg("regular expression match for '%s' failed: %s", file_ident_user+1, errstr))); |
| 1466 | + *error_p = true; |
| 1467 | + } |
| 1468 | + |
| 1469 | + pfree(wstr); |
| 1470 | + pg_regfree(&re); |
| 1471 | + return; |
| 1472 | + } |
| 1473 | + pfree(wstr); |
| 1474 | + |
| 1475 | + if ((ofs = strstr(file_pgrole, "\\1")) != NULL) |
| 1476 | + { |
| 1477 | + /* substitution of the first argument requested */ |
| 1478 | + if (matches[1].rm_so < 0) |
| 1479 | + ereport(ERROR, |
| 1480 | + (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION), |
| 1481 | + errmsg("regular expression '%s' has no subexpressions as requested by backreference in '%s'", |
| 1482 | + file_ident_user+1, file_pgrole))); |
| 1483 | + /* length: original length minus length of \1 plus length of match plus null terminator */ |
| 1484 | + regexp_pgrole = palloc0(strlen(file_pgrole) - 2 + (matches[1].rm_eo-matches[1].rm_so) + 1); |
| 1485 | + strncpy(regexp_pgrole, file_pgrole, (ofs-file_pgrole)); |
| 1486 | + memcpy(regexp_pgrole+strlen(regexp_pgrole), |
| 1487 | + ident_user+matches[1].rm_so, |
| 1488 | + matches[1].rm_eo-matches[1].rm_so); |
| 1489 | + strcat(regexp_pgrole, ofs+2); |
| 1490 | + } |
| 1491 | + else |
| 1492 | + { |
| 1493 | + /* no substitution, so copy the match */ |
| 1494 | + regexp_pgrole = pstrdup(file_pgrole); |
| 1495 | + } |
| 1496 | + |
| 1497 | + pg_regfree(&re); |
| 1498 | + |
| 1499 | + /* now check if the username actually matched what the user is trying to connect as */ |
| 1500 | + if (case_insensitive) |
| 1501 | + { |
| 1502 | + if (pg_strcasecmp(regexp_pgrole, pg_role) == 0) |
| 1503 | + *found_p = true; |
| 1504 | + } |
| 1505 | + else |
| 1506 | + { |
| 1507 | + if (strcmp(regexp_pgrole, pg_role) == 0) |
| 1508 | + *found_p = true; |
| 1509 | + } |
| 1510 | + pfree(regexp_pgrole); |
| 1511 | + |
| 1512 | + return; |
1413 | 1513 | }
|
1414 | 1514 | else
|
1415 | 1515 | {
|
1416 |
| - if (strcmp(file_map, usermap_name) == 0 && |
1417 |
| - strcmp(file_pgrole, pg_role) == 0 && |
1418 |
| - strcmp(file_ident_user, ident_user) == 0) |
1419 |
| - *found_p = true; |
| 1516 | + /* Not regular expression, so make complete match */ |
| 1517 | + if (case_insensitive) |
| 1518 | + { |
| 1519 | + if (pg_strcasecmp(file_pgrole, pg_role) == 0 && |
| 1520 | + pg_strcasecmp(file_ident_user, ident_user) == 0) |
| 1521 | + *found_p = true; |
| 1522 | + } |
| 1523 | + else |
| 1524 | + { |
| 1525 | + if (strcmp(file_pgrole, pg_role) == 0 && |
| 1526 | + strcmp(file_ident_user, ident_user) == 0) |
| 1527 | + *found_p = true; |
| 1528 | + } |
1420 | 1529 | }
|
1421 | 1530 |
|
1422 | 1531 | return;
|
|
0 commit comments