|
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 intrecv_and_check_passwordv0(Port *port);
|
43 | 43 |
|
44 | 44 | char*pg_krb_server_keyfile;
|
45 | 45 |
|
| 46 | +#ifdefUSE_PAM |
| 47 | +#include<security/pam_appl.h> |
| 48 | + |
| 49 | +#definePGSQL_PAM_SERVICE "postgresql"/* Service name passed to PAM */ |
| 50 | + |
| 51 | +staticintCheckPAMAuth(Port*port,char*user,char*password); |
| 52 | +staticintpam_passwd_conv_proc(intnum_msg,conststructpam_message**msg, |
| 53 | +structpam_response**resp,void*appdata_ptr); |
| 54 | + |
| 55 | +staticstructpam_convpam_passw_conv= { |
| 56 | +&pam_passwd_conv_proc, |
| 57 | +NULL |
| 58 | +}; |
| 59 | + |
| 60 | +staticchar*pam_passwd=NULL;/* Workaround for Solaris 2.6 brokenness */ |
| 61 | +staticPort*pam_port_cludge;/* Workaround for passing "Port |
| 62 | + * *port" into pam_passwd_conv_proc */ |
| 63 | +#endif/* USE_PAM */ |
46 | 64 |
|
47 | 65 | #ifdefKRB4
|
48 | 66 | /*----------------------------------------------------------------
|
@@ -428,6 +446,11 @@ auth_failed(Port *port)
|
428 | 446 | caseuaPassword:
|
429 | 447 | authmethod="Password";
|
430 | 448 | break;
|
| 449 | +#ifdefUSE_PAM |
| 450 | +caseuaPAM: |
| 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 |
| -caseuaCrypt: |
529 |
| -sendAuthRequest(port,AUTH_REQ_CRYPT); |
530 |
| -status=recv_and_check_password_packet(port); |
531 |
| -break; |
532 |
| - |
533 |
| -caseuaPassword: |
534 |
| -sendAuthRequest(port,AUTH_REQ_PASSWORD); |
535 |
| -status=recv_and_check_password_packet(port); |
| 551 | +caseuaCrypt: |
| 552 | +sendAuthRequest(port,AUTH_REQ_CRYPT); |
| 553 | +status=recv_and_check_password_packet(port); |
| 554 | +break; |
| 555 | + |
| 556 | +caseuaPassword: |
| 557 | +sendAuthRequest(port,AUTH_REQ_PASSWORD); |
| 558 | +status=recv_and_check_password_packet(port); |
| 559 | +break; |
| 560 | +#ifdefUSE_PAM |
| 561 | +caseuaPAM: |
| 562 | +pam_port_cludge=port; |
| 563 | +status=CheckPAMAuth(port,port->user,""); |
536 | 564 | break;
|
| 565 | +#endif/* USE_PAM */ |
537 | 566 |
|
538 | 567 | caseuaTrust:
|
539 | 568 | status=STATUS_OK;
|
@@ -577,7 +606,190 @@ sendAuthRequest(Port *port, AuthRequest areq)
|
577 | 606 | pq_flush();
|
578 | 607 | }
|
579 | 608 |
|
| 609 | +#ifdefUSE_PAM |
| 610 | + |
| 611 | +/* |
| 612 | + * PAM conversation function |
| 613 | + */ |
| 614 | + |
| 615 | +staticint |
| 616 | +pam_passwd_conv_proc (intnum_msg,conststructpam_message**msg,structpam_response**resp,void*appdata_ptr) |
| 617 | +{ |
| 618 | +StringInfoDatabuf; |
| 619 | +int32len; |
| 620 | + |
| 621 | +if (num_msg!=1||msg[0]->msg_style!=PAM_PROMPT_ECHO_OFF) { |
| 622 | +switch(msg[0]->msg_style) { |
| 623 | +casePAM_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 | +returnPAM_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 | +returnPAM_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 | +returnPAM_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 | +returnPAM_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(structpam_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 | +returnPAM_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 | +staticint |
| 694 | +CheckPAMAuth(Port*port,char*user,char*password) |
| 695 | +{ |
| 696 | +intretval; |
| 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 | +returnSTATUS_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 | +returnSTATUS_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 | +returnSTATUS_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 | +returnSTATUS_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 | +returnSTATUS_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 | +returnSTATUS_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 | caseuaMD5:
|
671 | 883 | caseuaCrypt:
|
672 | 884 | caseuaReject:
|
| 885 | +#ifdefUSE_PAM |
| 886 | +caseuaPAM: |
| 887 | +#endif/* USE_PAM */ |
673 | 888 | status=STATUS_ERROR;
|
674 | 889 | break;
|
675 | 890 |
|
|