bec5c5744da9f7e2fe3b54dfc06ef3437a0e0bbc
[exim.git] / src / src / auths / call_pam.c
1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 1995 - 2009 */
6 /* See the file NOTICE for conditions of use and distribution. */
7
8 #include "../exim.h"
9
10 /* This module contains functions that call the PAM authentication mechanism
11 defined by Sun for Solaris and also available for Linux and other OS.
12
13 We can't just compile this code and allow the library mechanism to omit the
14 functions if they are not wanted, because we need to have the PAM headers
15 available for compiling. Therefore, compile these functions only if SUPPORT_PAM
16 is defined. However, some compilers don't like compiling empty modules, so keep
17 them happy with a dummy when skipping the rest. Make it reference itself to
18 stop picky compilers complaining that it is unused, and put in a dummy argument
19 to stop even pickier compilers complaining about infinite loops. */
20
21 #ifndef SUPPORT_PAM
22 static void dummy(int x) { dummy(x-1); }
23 #else /* SUPPORT_PAM */
24
25 #ifdef PAM_H_IN_PAM
26 #include <pam/pam_appl.h>
27 #else
28 #include <security/pam_appl.h>
29 #endif
30
31 /* According to the specification, it should be possible to have an application
32 data pointer passed to the conversation function. However, I was unable to get
33 this to work on Solaris 2.6, so static variables are used instead. */
34
35 static int pam_conv_had_error;
36 static uschar *pam_args;
37 static BOOL pam_arg_ended;
38
39
40
41 /*************************************************
42 * PAM conversation function *
43 *************************************************/
44
45 /* This function is passed to the PAM authentication function, and it calls it
46 back when it wants data from the client. The string list is in pam_args. When
47 we reach the end, we pass back an empty string once. If this function is called
48 again, it will give an error response. This is protection against something
49 crazy happening.
50
51 Arguments:
52 num_msg number of messages associated with the call
53 msg points to an array of length num_msg of pam_message structures
54 resp set to point to the response block, which has to be got by
55 this function
56 appdata_ptr the application data pointer - not used because in Solaris
57 2.6 it always arrived in pam_converse() as NULL
58
59 Returns: a PAM return code
60 */
61
62 static int
63 pam_converse (int num_msg, PAM_CONVERSE_ARG2_TYPE **msg,
64 struct pam_response **resp, void *appdata_ptr)
65 {
66 int i;
67 int sep = 0;
68 struct pam_response *reply;
69
70 if (pam_arg_ended) return PAM_CONV_ERR;
71
72 reply = malloc(sizeof(struct pam_response) * num_msg);
73
74 if (reply == NULL) return PAM_CONV_ERR;
75
76 for (i = 0; i < num_msg; i++)
77 {
78 uschar *arg;
79 switch (msg[i]->msg_style)
80 {
81 case PAM_PROMPT_ECHO_ON:
82 case PAM_PROMPT_ECHO_OFF:
83 arg = string_nextinlist(&pam_args, &sep, big_buffer, big_buffer_size);
84 if (arg == NULL)
85 {
86 arg = US"";
87 pam_arg_ended = TRUE;
88 }
89 reply[i].resp = CS string_copy_malloc(arg); /* PAM frees resp */
90 reply[i].resp_retcode = PAM_SUCCESS;
91 break;
92
93 case PAM_TEXT_INFO: /* Just acknowledge messages */
94 case PAM_ERROR_MSG:
95 reply[i].resp_retcode = PAM_SUCCESS;
96 reply[i].resp = NULL;
97 break;
98
99 default: /* Must be an error of some sort... */
100 free (reply);
101 pam_conv_had_error = TRUE;
102 return PAM_CONV_ERR;
103 }
104 }
105
106 *resp = reply;
107 return PAM_SUCCESS;
108 }
109
110
111
112 /*************************************************
113 * Perform PAM authentication *
114 *************************************************/
115
116 /* This function calls the PAM authentication mechanism, passing over one or
117 more data strings.
118
119 Arguments:
120 s a colon-separated list of strings
121 errptr where to point an error message
122
123 Returns: OK if authentication succeeded
124 FAIL if authentication failed
125 ERROR some other error condition
126 */
127
128 int
129 auth_call_pam(uschar *s, uschar **errptr)
130 {
131 pam_handle_t *pamh = NULL;
132 struct pam_conv pamc;
133 int pam_error;
134 int sep = 0;
135 uschar *user;
136
137 /* Set up the input data structure: the address of the conversation function,
138 and a pointer to application data, which we don't use because I couldn't get it
139 to work under Solaris 2.6 - it always arrived in pam_converse() as NULL. */
140
141 pamc.conv = pam_converse;
142 pamc.appdata_ptr = NULL;
143
144 /* Initialize the static data - the current input data, the error flag, and the
145 flag for data end. */
146
147 pam_args = s;
148 pam_conv_had_error = FALSE;
149 pam_arg_ended = FALSE;
150
151 /* The first string in the list is the user. If this is an empty string, we
152 fail. PAM doesn't support authentication with an empty user (it prompts for it,
153 causing a potential mis-interpretation). */
154
155 user = string_nextinlist(&pam_args, &sep, big_buffer, big_buffer_size);
156 if (user == NULL || user[0] == 0) return FAIL;
157
158 /* Start off PAM interaction */
159
160 DEBUG(D_auth)
161 debug_printf("Running PAM authentication for user \"%s\"\n", user);
162
163 pam_error = pam_start ("exim", CS user, &pamc, &pamh);
164
165 /* Do the authentication - the pam_authenticate() will call pam_converse() to
166 get the data it wants. After successful authentication we call pam_acct_mgmt()
167 to apply any other restrictions (e.g. only some times of day). */
168
169 if (pam_error == PAM_SUCCESS)
170 {
171 pam_error = pam_authenticate (pamh, PAM_SILENT);
172 if (pam_error == PAM_SUCCESS && !pam_conv_had_error)
173 pam_error = pam_acct_mgmt (pamh, PAM_SILENT);
174 }
175
176 /* Finish the PAM interaction - this causes it to clean up store etc. Unclear
177 what should be passed as the second argument. */
178
179 pam_end(pamh, PAM_SUCCESS);
180
181 /* Sort out the return code. If not success, set the error message. */
182
183 if (pam_error == PAM_SUCCESS)
184 {
185 DEBUG(D_auth) debug_printf("PAM success\n");
186 return OK;
187 }
188
189 *errptr = (uschar *)pam_strerror(pamh, pam_error);
190 DEBUG(D_auth) debug_printf("PAM error: %s\n", *errptr);
191
192 if (pam_error == PAM_USER_UNKNOWN ||
193 pam_error == PAM_AUTH_ERR ||
194 pam_error == PAM_ACCT_EXPIRED)
195 return FAIL;
196
197 return ERROR;
198 }
199
200 #endif /* SUPPORT_PAM */
201
202 /* End of call_pam.c */