|
8 | 8 | *
|
9 | 9 | *
|
10 | 10 | * IDENTIFICATION
|
11 |
| - * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.64 2001/08/21 15:21:25 momjian Exp $ |
| 11 | + * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.65 2001/09/06 03:23:38 momjian Exp $ |
12 | 12 | *
|
13 | 13 | *-------------------------------------------------------------------------
|
14 | 14 | */
|
@@ -43,6 +43,24 @@ static int recv_and_check_passwordv0(Port *port);
|
43 | 43 |
|
44 | 44 | char *pg_krb_server_keyfile;
|
45 | 45 |
|
| 46 | +#ifdef USE_PAM |
| 47 | +#include <security/pam_appl.h> |
| 48 | + |
| 49 | +#define PGSQL_PAM_SERVICE "postgresql" /* Service name passed to PAM */ |
| 50 | + |
| 51 | +static int CheckPAMAuth(Port *port, char *user, char *password); |
| 52 | +static int pam_passwd_conv_proc(int num_msg, const struct pam_message **msg, |
| 53 | + struct pam_response **resp, void *appdata_ptr); |
| 54 | + |
| 55 | +static struct pam_conv pam_passw_conv = { |
| 56 | + &pam_passwd_conv_proc, |
| 57 | + NULL |
| 58 | +}; |
| 59 | + |
| 60 | +static char * pam_passwd = NULL; /* Workaround for Solaris 2.6 brokenness */ |
| 61 | +static Port * pam_port_cludge; /* Workaround for passing "Port |
| 62 | + * *port" into pam_passwd_conv_proc */ |
| 63 | +#endif /* USE_PAM */ |
46 | 64 |
|
47 | 65 | #ifdef KRB4
|
48 | 66 | /*----------------------------------------------------------------
|
@@ -428,6 +446,11 @@ auth_failed(Port *port)
|
428 | 446 | case uaPassword:
|
429 | 447 | authmethod = "Password";
|
430 | 448 | break;
|
| 449 | +#ifdef USE_PAM |
| 450 | + case uaPAM: |
| 451 | + authmethod = "PAM"; |
| 452 | + break; |
| 453 | +#endif /* USE_PAM */ |
431 | 454 | }
|
432 | 455 |
|
433 | 456 | elog(FATAL, "%s authentication failed for user \"%s\"",
|
@@ -525,15 +548,21 @@ ClientAuthentication(Port *port)
|
525 | 548 | status = recv_and_check_password_packet(port);
|
526 | 549 | break;
|
527 | 550 |
|
528 |
| - case uaCrypt: |
529 |
| - sendAuthRequest(port, AUTH_REQ_CRYPT); |
530 |
| - status = recv_and_check_password_packet(port); |
531 |
| - break; |
532 |
| - |
533 |
| - case uaPassword: |
534 |
| - sendAuthRequest(port, AUTH_REQ_PASSWORD); |
535 |
| - status = recv_and_check_password_packet(port); |
| 551 | + case uaCrypt: |
| 552 | + sendAuthRequest(port, AUTH_REQ_CRYPT); |
| 553 | + status = recv_and_check_password_packet(port); |
| 554 | + break; |
| 555 | + |
| 556 | + case uaPassword: |
| 557 | + sendAuthRequest(port, AUTH_REQ_PASSWORD); |
| 558 | + status = recv_and_check_password_packet(port); |
| 559 | + break; |
| 560 | +#ifdef USE_PAM |
| 561 | + case uaPAM: |
| 562 | + pam_port_cludge = port; |
| 563 | + status = CheckPAMAuth(port, port->user, ""); |
536 | 564 | break;
|
| 565 | +#endif /* USE_PAM */ |
537 | 566 |
|
538 | 567 | case uaTrust:
|
539 | 568 | status = STATUS_OK;
|
@@ -577,7 +606,190 @@ sendAuthRequest(Port *port, AuthRequest areq)
|
577 | 606 | pq_flush();
|
578 | 607 | }
|
579 | 608 |
|
| 609 | +#ifdef USE_PAM |
| 610 | + |
| 611 | +/* |
| 612 | + * PAM conversation function |
| 613 | + */ |
| 614 | + |
| 615 | +static int |
| 616 | +pam_passwd_conv_proc (int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) |
| 617 | +{ |
| 618 | + StringInfoData buf; |
| 619 | + int32 len; |
| 620 | + |
| 621 | + if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF) { |
| 622 | + switch(msg[0]->msg_style) { |
| 623 | + case PAM_ERROR_MSG: |
| 624 | + snprintf(PQerrormsg, PQERRORMSG_LENGTH, |
| 625 | + "pam_passwd_conv_proc: Error from underlying PAM layer: '%s'\n", msg[0]->msg); |
| 626 | + fputs(PQerrormsg, stderr); |
| 627 | + pqdebug("%s", PQerrormsg); |
| 628 | + return PAM_CONV_ERR; |
| 629 | + default: |
| 630 | + snprintf(PQerrormsg, PQERRORMSG_LENGTH, |
| 631 | + "pam_passwd_conv_proc: Unexpected PAM conversation %d/'%s'\n", |
| 632 | + msg[0]->msg_style, msg[0]->msg); |
| 633 | + fputs(PQerrormsg, stderr); |
| 634 | + pqdebug("%s", PQerrormsg); |
| 635 | + return PAM_CONV_ERR; |
| 636 | + } |
| 637 | + } |
| 638 | + |
| 639 | + if (!appdata_ptr) { |
| 640 | + /* Workaround for Solaris 2.6 where the PAM library is broken |
| 641 | + * and does not pass appdata_ptr to the conversation routine |
| 642 | + */ |
| 643 | + appdata_ptr = pam_passwd; |
| 644 | + } |
| 645 | + |
| 646 | + /* Password wasn't passed to PAM the first time around - let's go |
| 647 | + * ask the client to send a password, which we then stuff into |
| 648 | + * PAM. |
| 649 | + */ |
| 650 | + if(strlen(appdata_ptr) == 0) { |
| 651 | + sendAuthRequest(pam_port_cludge, AUTH_REQ_PASSWORD); |
| 652 | + if (pq_eof() == EOF || pq_getint(&len, 4) == EOF) { |
| 653 | + return PAM_CONV_ERR; /* client didn't want to send password */ |
| 654 | + } |
| 655 | + |
| 656 | + initStringInfo(&buf); |
| 657 | + pq_getstr(&buf); |
| 658 | + if (DebugLvl) |
| 659 | + fprintf(stderr, "received PAM packet with len=%d, pw=%s\n", |
| 660 | + len, buf.data); |
| 661 | + |
| 662 | + if(strlen(buf.data) == 0) { |
| 663 | + snprintf(PQerrormsg, PQERRORMSG_LENGTH, "pam_passwd_conv_proc: no password\n"); |
| 664 | + fputs(PQerrormsg, stderr); |
| 665 | + return PAM_CONV_ERR; |
| 666 | + } |
| 667 | + appdata_ptr = buf.data; |
| 668 | + } |
| 669 | + |
| 670 | + /* Explicitly not using palloc here - PAM will free this memory in |
| 671 | + * pam_end() |
| 672 | + */ |
| 673 | + *resp = calloc(num_msg, sizeof(struct pam_response)); |
| 674 | + if (!*resp) { |
| 675 | + snprintf(PQerrormsg, PQERRORMSG_LENGTH, "pam_passwd_conv_proc: Out of memory!\n"); |
| 676 | + fputs(PQerrormsg, stderr); |
| 677 | + pqdebug("%s", PQerrormsg); |
| 678 | + if(buf.data) |
| 679 | + pfree(buf.data); |
| 680 | + return PAM_CONV_ERR; |
| 681 | + } |
| 682 | + |
| 683 | + (*resp)[0].resp = strdup((char *) appdata_ptr); |
| 684 | + (*resp)[0].resp_retcode = 0; |
| 685 | + |
| 686 | + return ((*resp)[0].resp ? PAM_SUCCESS : PAM_CONV_ERR); |
| 687 | +} |
| 688 | + |
| 689 | + |
| 690 | +/* |
| 691 | + * Check authentication against PAM. |
| 692 | + */ |
| 693 | +static int |
| 694 | +CheckPAMAuth(Port *port, char *user, char *password) |
| 695 | +{ |
| 696 | + int retval; |
| 697 | + pam_handle_t *pamh = NULL; |
| 698 | + |
| 699 | + /* |
| 700 | + * Apparently, Solaris 2.6 is broken, and needs ugly static |
| 701 | + * variable workaround |
| 702 | + */ |
| 703 | + pam_passwd = password; |
| 704 | + |
| 705 | + /* Set the application data portion of the conversation struct |
| 706 | + * This is later used inside the PAM conversation to pass the |
| 707 | + * password to the authentication module. |
| 708 | + */ |
| 709 | + pam_passw_conv.appdata_ptr = (char*) password; /* from password above, not allocated */ |
| 710 | + |
| 711 | + /* Optionally, one can set the service name in pg_hba.conf */ |
| 712 | + if(port->auth_arg[0] == '\0') { |
| 713 | + retval = pam_start(PGSQL_PAM_SERVICE, "pgsql@", &pam_passw_conv, &pamh); |
| 714 | + } else { |
| 715 | + retval = pam_start(port->auth_arg, "pgsql@", &pam_passw_conv, &pamh); |
| 716 | + } |
| 717 | + |
| 718 | + if (retval != PAM_SUCCESS) { |
| 719 | + snprintf(PQerrormsg, PQERRORMSG_LENGTH, |
| 720 | + "CheckPAMAuth: Failed to create PAM authenticator: '%s'\n", |
| 721 | + pam_strerror(pamh, retval)); |
| 722 | + fputs(PQerrormsg, stderr); |
| 723 | + pqdebug("%s", PQerrormsg); |
| 724 | + pam_passwd = NULL; /* Unset pam_passwd */ |
| 725 | + return STATUS_ERROR; |
| 726 | + } |
| 727 | + |
| 728 | + if (retval == PAM_SUCCESS) { |
| 729 | + retval = pam_set_item(pamh, PAM_USER, user); |
| 730 | + } else { |
| 731 | + snprintf(PQerrormsg, PQERRORMSG_LENGTH, |
| 732 | + "CheckPAMAuth: pam_set_item(PAM_USER) failed: '%s'\n", |
| 733 | + pam_strerror(pamh, retval)); |
| 734 | + fputs(PQerrormsg, stderr); |
| 735 | + pqdebug("%s", PQerrormsg); |
| 736 | + pam_passwd = NULL; /* Unset pam_passwd */ |
| 737 | + return STATUS_ERROR; |
| 738 | + } |
| 739 | + if (retval == PAM_SUCCESS) { |
| 740 | + retval = pam_set_item(pamh, PAM_CONV, &pam_passw_conv); |
| 741 | + } else { |
| 742 | + snprintf(PQerrormsg, PQERRORMSG_LENGTH, |
| 743 | + "CheckPAMAuth: pam_set_item(PAM_CONV) failed: '%s'\n", |
| 744 | + pam_strerror(pamh, retval)); |
| 745 | + fputs(PQerrormsg, stderr); |
| 746 | + pqdebug("%s", PQerrormsg); |
| 747 | + pam_passwd = NULL; /* Unset pam_passwd */ |
| 748 | + return STATUS_ERROR; |
| 749 | + } |
| 750 | + if (retval == PAM_SUCCESS) { |
| 751 | + retval = pam_authenticate(pamh, 0); |
| 752 | + } else { |
| 753 | + snprintf(PQerrormsg, PQERRORMSG_LENGTH, |
| 754 | + "CheckPAMAuth: pam_authenticate failed: '%s'\n", |
| 755 | + pam_strerror(pamh, retval)); |
| 756 | + fputs(PQerrormsg, stderr); |
| 757 | + pqdebug("%s", PQerrormsg); |
| 758 | + pam_passwd = NULL; /* Unset pam_passwd */ |
| 759 | + return STATUS_ERROR; |
| 760 | + } |
| 761 | + if (retval == PAM_SUCCESS) { |
| 762 | + retval = pam_acct_mgmt(pamh, 0); |
| 763 | + } else { |
| 764 | + snprintf(PQerrormsg, PQERRORMSG_LENGTH, |
| 765 | + "CheckPAMAuth: pam_acct_mgmt failed: '%s'\n", |
| 766 | + pam_strerror(pamh, retval)); |
| 767 | + fputs(PQerrormsg, stderr); |
| 768 | + pqdebug("%s", PQerrormsg); |
| 769 | + pam_passwd = NULL; /* Unset pam_passwd */ |
| 770 | + return STATUS_ERROR; |
| 771 | + } |
| 772 | + if (retval == PAM_SUCCESS) { |
| 773 | + retval = pam_end(pamh, retval); |
| 774 | + if(retval != PAM_SUCCESS) { |
| 775 | + snprintf(PQerrormsg, PQERRORMSG_LENGTH, |
| 776 | + "CheckPAMAuth: Failed to release PAM authenticator: '%s'\n", |
| 777 | + pam_strerror(pamh, retval)); |
| 778 | + fputs(PQerrormsg, stderr); |
| 779 | + pqdebug("%s", PQerrormsg); |
| 780 | + } |
| 781 | + |
| 782 | + pam_passwd = NULL; /* Unset pam_passwd */ |
| 783 | + |
| 784 | + return (retval == PAM_SUCCESS ? STATUS_OK : STATUS_ERROR); |
| 785 | + } else { |
| 786 | + return STATUS_ERROR; |
| 787 | + } |
| 788 | +} |
| 789 | + |
| 790 | + |
580 | 791 |
|
| 792 | +#endif /* USE_PAM */ |
581 | 793 |
|
582 | 794 | /*
|
583 | 795 | * Called when we have received the password packet.
|
@@ -670,6 +882,9 @@ map_old_to_new(Port *port, UserAuth old, int status)
|
670 | 882 | case uaMD5:
|
671 | 883 | case uaCrypt:
|
672 | 884 | case uaReject:
|
| 885 | +#ifdef USE_PAM |
| 886 | + case uaPAM: |
| 887 | +#endif /* USE_PAM */ |
673 | 888 | status = STATUS_ERROR;
|
674 | 889 | break;
|
675 | 890 |
|
|
0 commit comments