1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) University of Cambridge 1995 - 2018 */
6 /* Copyright (c) The Exim Maintainers 2020 - 2021 */
7 /* See the file NOTICE for conditions of use and distribution. */
11 /* This module contains functions that call the PAM authentication mechanism
12 defined by Sun for Solaris and also available for Linux and other OS.
14 We can't just compile this code and allow the library mechanism to omit the
15 functions if they are not wanted, because we need to have the PAM headers
16 available for compiling. Therefore, compile these functions only if SUPPORT_PAM
17 is defined. However, some compilers don't like compiling empty modules, so keep
18 them happy with a dummy when skipping the rest. Make it reference itself to
19 stop picky compilers complaining that it is unused, and put in a dummy argument
20 to stop even pickier compilers complaining about infinite loops.
21 Then use a mutually-recursive pair as gcc is just getting stupid. */
24 static void dummy(int x);
25 static void dummy2(int x) { dummy(x-1); }
26 static void dummy(int x) { dummy2(x-1); }
27 #else /* SUPPORT_PAM */
30 #include <pam/pam_appl.h>
32 #include <security/pam_appl.h>
35 /* According to the specification, it should be possible to have an application
36 data pointer passed to the conversation function. However, I was unable to get
37 this to work on Solaris 2.6, so static variables are used instead. */
39 static int pam_conv_had_error;
40 static const uschar *pam_args;
41 static BOOL pam_arg_ended;
45 /*************************************************
46 * PAM conversation function *
47 *************************************************/
49 /* This function is passed to the PAM authentication function, and it calls it
50 back when it wants data from the client. The string list is in pam_args. When
51 we reach the end, we pass back an empty string once. If this function is called
52 again, it will give an error response. This is protection against something
56 num_msg number of messages associated with the call
57 msg points to an array of length num_msg of pam_message structures
58 resp set to point to the response block, which has to be got by
60 appdata_ptr the application data pointer - not used because in Solaris
61 2.6 it always arrived in pam_converse() as NULL
63 Returns: a PAM return code
67 pam_converse (int num_msg, PAM_CONVERSE_ARG2_TYPE **msg,
68 struct pam_response **resp, void *appdata_ptr)
71 struct pam_response *reply;
73 /* It seems that PAM frees reply[] */
76 || !(reply = malloc(sizeof(struct pam_response) * num_msg)))
79 for (int i = 0; i < num_msg; i++)
82 switch (msg[i]->msg_style)
84 case PAM_PROMPT_ECHO_ON:
85 case PAM_PROMPT_ECHO_OFF:
86 if (!(arg = string_nextinlist(&pam_args, &sep, NULL, 0)))
91 reply[i].resp = strdup(CCS arg); /* Use libc malloc, PAM frees resp directly*/
92 reply[i].resp_retcode = PAM_SUCCESS;
95 case PAM_TEXT_INFO: /* Just acknowledge messages */
97 reply[i].resp_retcode = PAM_SUCCESS;
101 default: /* Must be an error of some sort... */
103 pam_conv_had_error = TRUE;
114 /*************************************************
115 * Perform PAM authentication *
116 *************************************************/
118 /* This function calls the PAM authentication mechanism, passing over one or
122 s a colon-separated list of strings
123 errptr where to point an error message
125 Returns: OK if authentication succeeded
126 FAIL if authentication failed
127 ERROR some other error condition
131 auth_call_pam(const uschar *s, uschar **errptr)
133 pam_handle_t *pamh = NULL;
134 struct pam_conv pamc;
139 /* Set up the input data structure: the address of the conversation function,
140 and a pointer to application data, which we don't use because I couldn't get it
141 to work under Solaris 2.6 - it always arrived in pam_converse() as NULL. */
143 pamc.conv = pam_converse;
144 pamc.appdata_ptr = NULL;
146 /* Initialize the static data - the current input data, the error flag, and the
147 flag for data end. */
150 pam_conv_had_error = FALSE;
151 pam_arg_ended = FALSE;
153 /* The first string in the list is the user. If this is an empty string, we
154 fail. PAM doesn't support authentication with an empty user (it prompts for it,
155 causing a potential mis-interpretation). */
157 user = string_nextinlist(&pam_args, &sep, NULL, 0);
158 if (user == NULL || user[0] == 0) return FAIL;
160 /* Start off PAM interaction */
163 debug_printf("Running PAM authentication for user \"%s\"\n", user);
165 pam_error = pam_start ("exim", CS user, &pamc, &pamh);
167 /* Do the authentication - the pam_authenticate() will call pam_converse() to
168 get the data it wants. After successful authentication we call pam_acct_mgmt()
169 to apply any other restrictions (e.g. only some times of day). */
171 if (pam_error == PAM_SUCCESS)
173 pam_error = pam_authenticate (pamh, PAM_SILENT);
174 if (pam_error == PAM_SUCCESS && !pam_conv_had_error)
175 pam_error = pam_acct_mgmt (pamh, PAM_SILENT);
178 /* Finish the PAM interaction - this causes it to clean up store etc. Unclear
179 what should be passed as the second argument. */
181 pam_end(pamh, PAM_SUCCESS);
183 /* Sort out the return code. If not success, set the error message. */
185 if (pam_error == PAM_SUCCESS)
187 DEBUG(D_auth) debug_printf("PAM success\n");
191 *errptr = US pam_strerror(pamh, pam_error);
192 DEBUG(D_auth) debug_printf("PAM error: %s\n", *errptr);
194 if (pam_error == PAM_USER_UNKNOWN ||
195 pam_error == PAM_AUTH_ERR ||
196 pam_error == PAM_ACCT_EXPIRED)
202 #endif /* SUPPORT_PAM */
204 /* End of call_pam.c */