More compiler quietening.
[exim.git] / src / src / auths / call_pam.c
... / ...
CommitLineData
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
11defined by Sun for Solaris and also available for Linux and other OS.
12
13We can't just compile this code and allow the library mechanism to omit the
14functions if they are not wanted, because we need to have the PAM headers
15available for compiling. Therefore, compile these functions only if SUPPORT_PAM
16is defined. However, some compilers don't like compiling empty modules, so keep
17them happy with a dummy when skipping the rest. Make it reference itself to
18stop picky compilers complaining that it is unused, and put in a dummy argument
19to stop even pickier compilers complaining about infinite loops. */
20
21#ifndef SUPPORT_PAM
22static 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
32data pointer passed to the conversation function. However, I was unable to get
33this to work on Solaris 2.6, so static variables are used instead. */
34
35static int pam_conv_had_error;
36static uschar *pam_args;
37static 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
46back when it wants data from the client. The string list is in pam_args. When
47we reach the end, we pass back an empty string once. If this function is called
48again, it will give an error response. This is protection against something
49crazy happening.
50
51Arguments:
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
59Returns: a PAM return code
60*/
61
62static int
63pam_converse (int num_msg, PAM_CONVERSE_ARG2_TYPE **msg,
64 struct pam_response **resp, void *appdata_ptr)
65{
66int i;
67int sep = 0;
68struct pam_response *reply;
69
70if (pam_arg_ended) return PAM_CONV_ERR;
71
72reply = malloc(sizeof(struct pam_response) * num_msg);
73
74if (reply == NULL) return PAM_CONV_ERR;
75
76for (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;
107return 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
117more data strings.
118
119Arguments:
120 s a colon-separated list of strings
121 errptr where to point an error message
122
123Returns: OK if authentication succeeded
124 FAIL if authentication failed
125 ERROR some other error condition
126*/
127
128int
129auth_call_pam(uschar *s, uschar **errptr)
130{
131pam_handle_t *pamh = NULL;
132struct pam_conv pamc;
133int pam_error;
134int sep = 0;
135uschar *user;
136
137/* Set up the input data structure: the address of the conversation function,
138and a pointer to application data, which we don't use because I couldn't get it
139to work under Solaris 2.6 - it always arrived in pam_converse() as NULL. */
140
141pamc.conv = pam_converse;
142pamc.appdata_ptr = NULL;
143
144/* Initialize the static data - the current input data, the error flag, and the
145flag for data end. */
146
147pam_args = s;
148pam_conv_had_error = FALSE;
149pam_arg_ended = FALSE;
150
151/* The first string in the list is the user. If this is an empty string, we
152fail. PAM doesn't support authentication with an empty user (it prompts for it,
153causing a potential mis-interpretation). */
154
155user = string_nextinlist(&pam_args, &sep, big_buffer, big_buffer_size);
156if (user == NULL || user[0] == 0) return FAIL;
157
158/* Start off PAM interaction */
159
160DEBUG(D_auth)
161 debug_printf("Running PAM authentication for user \"%s\"\n", user);
162
163pam_error = pam_start ("exim", CS user, &pamc, &pamh);
164
165/* Do the authentication - the pam_authenticate() will call pam_converse() to
166get the data it wants. After successful authentication we call pam_acct_mgmt()
167to apply any other restrictions (e.g. only some times of day). */
168
169if (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
177what should be passed as the second argument. */
178
179pam_end(pamh, PAM_SUCCESS);
180
181/* Sort out the return code. If not success, set the error message. */
182
183if (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);
190DEBUG(D_auth) debug_printf("PAM error: %s\n", *errptr);
191
192if (pam_error == PAM_USER_UNKNOWN ||
193 pam_error == PAM_AUTH_ERR ||
194 pam_error == PAM_ACCT_EXPIRED)
195 return FAIL;
196
197return ERROR;
198}
199
200#endif /* SUPPORT_PAM */
201
202/* End of call_pam.c */