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