Commit | Line | Data |
---|---|---|
0a49a7a4 | 1 | /* $Cambridge: exim/src/src/auths/call_pam.c,v 1.5 2009/11/16 19:50:38 nm4 Exp $ */ |
0756eb3c PH |
2 | |
3 | /************************************************* | |
4 | * Exim - an Internet mail transport agent * | |
5 | *************************************************/ | |
6 | ||
0a49a7a4 | 7 | /* Copyright (c) University of Cambridge 1995 - 2009 */ |
0756eb3c PH |
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 */ |