1 /*
2 This program is free software; you can redistribute it and/or
3 modify it under the terms of the GNU General Public License as
4 published by the Free Software Foundation; either version 2, or (at
5 your option) any later version.
6
7 This program is distributed in the hope that it will be useful, but
8 WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 General Public License for more details.
11
12 Copyright (c) Alexey Mahotkin <alexm@hsys.msk.ru> 2002-2004
13
14 PAM support for checkpassword-pam
15
16 */
17
18 #include <config.h>
19
20 #include "pam-support.h"
21
22 #include "logging.h"
23
24 #include <security/pam_appl.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 static const char* global_password;
29
30 static int
conversation(int num_msg,const struct pam_message ** msgs,struct pam_response ** resp,void * appdata_ptr)31 conversation (int num_msg, const struct pam_message **msgs,
32 struct pam_response **resp, void *appdata_ptr)
33 {
34 int i;
35 struct pam_response* responses;
36 (void) appdata_ptr;
37
38 /* safety check */
39 if (num_msg <= 0) {
40 fatal("Internal PAM error: num_msgs <= 0");
41 return PAM_CONV_ERR;
42 }
43
44 /* allocate array of responses */
45 responses = calloc(num_msg, sizeof(struct pam_response));
46 if (!responses) {
47 fatal("Out of memory");
48 return PAM_CONV_ERR;
49 }
50
51 for (i = 0; i < num_msg; i++) {
52 const struct pam_message *msg = msgs[i];
53 struct pam_response* response = &(responses[i]);
54 char* style = NULL;
55 switch (msg->msg_style) {
56 case PAM_PROMPT_ECHO_OFF: style = "PAM_PROMPT_ECHO_OFF"; break;
57 case PAM_PROMPT_ECHO_ON: style = "PAM_PROMPT_ECHO_ON"; break;
58 case PAM_ERROR_MSG: style = "PAM_ERROR_MSG"; break;
59 case PAM_TEXT_INFO: style = "PAM_TEXT_INFO"; break;
60 default: fatal("Internal error: invalid msg_style: %d", msg->msg_style); break;
61 }
62 debugging("conversation(): msg[%d], style %s, msg = \"%s\"", i, style, msg->msg);
63
64 switch (msg->msg_style) {
65 case PAM_PROMPT_ECHO_OFF:
66 /* reply with password */
67 response->resp = strdup(global_password);
68 if (!response->resp)
69 return PAM_CONV_ERR;
70 break;
71
72 default:
73 fatal("Internal error: unknown message style: '%s'", style);
74 return PAM_CONV_ERR;
75 }
76 response->resp_retcode = 0;
77 }
78
79 *resp = responses;
80
81 return PAM_SUCCESS;
82 }
83
84
85
86 int
authenticate_using_pam(const char * service_name,const char * username,const char * password)87 authenticate_using_pam (const char* service_name,
88 const char* username,
89 const char* password)
90 {
91 struct pam_conv pam_conversation = { conversation, NULL };
92 pam_handle_t* pamh;
93 int retval;
94 char *remoteip;
95
96 /* to be used later from conversation() */
97 global_password = password;
98
99 /* initialize the PAM library */
100 debugging("Initializing PAM library using service name '%s'", service_name);
101 retval = pam_start(service_name, username, &pam_conversation, &pamh);
102 if (retval != PAM_SUCCESS) {
103 fatal("Initialization failed: %s", pam_strerror(pamh, retval));
104 return 111;
105 }
106 debugging("PAM library initialization succeeded");
107
108 /* provided by tcpserver */
109 remoteip = getenv("TCPREMOTEIP");
110 if (remoteip) {
111 /* we don't care if this succeeds or not */
112 pam_set_item(pamh, PAM_RHOST, remoteip);
113 }
114
115 /* Authenticate the user */
116 retval = pam_authenticate(pamh, 0);
117 if (retval != PAM_SUCCESS) {
118 /* usually PAM itself logs auth failures, but we need to see
119 how it looks from our side */
120 if (opt_debugging)
121 fatal("Authentication failed: %s", pam_strerror(pamh, retval));
122 return 1;
123 }
124
125 debugging("Authentication passed");
126
127 retval = pam_acct_mgmt(pamh, 0);
128 if (retval != PAM_SUCCESS) {
129 fatal("PAM account management failed: %s", pam_strerror(pamh, retval));
130 return 1;
131 }
132 debugging("Account management succeeded");
133
134 retval = pam_setcred(pamh, PAM_ESTABLISH_CRED);
135 if (retval != PAM_SUCCESS) {
136 fatal("Setting credentials failed: %s", pam_strerror(pamh, retval));
137 return 1;
138 }
139 debugging("Setting PAM credentials succeeded");
140
141 retval = pam_open_session(pamh, PAM_SILENT);
142 if (retval != PAM_SUCCESS) {
143 fatal("Session opening failed: %s", pam_strerror(pamh, retval));
144 return 1;
145 }
146 debugging("PAM session opened");
147
148 retval = pam_close_session(pamh, PAM_SILENT);
149 if (retval != PAM_SUCCESS) {
150 fatal("Session closing failed: %s", pam_strerror(pamh, retval));
151 return 1;
152 }
153 debugging("PAM session closed");
154
155 /* terminate the PAM library */
156 debugging("Terminating PAM library");
157 retval = pam_end(pamh, retval);
158 if (retval != PAM_SUCCESS) {
159 fatal("Terminating PAM failed: %s", pam_strerror(pamh, retval));
160 return 1;
161 }
162
163 return 0;
164 }
165
166