Commit | Line | Data |
---|---|---|
0756eb3c PH |
1 | /************************************************* |
2 | * Exim - an Internet mail transport agent * | |
3 | *************************************************/ | |
4 | ||
0a49a7a4 | 5 | /* Copyright (c) University of Cambridge 1995 - 2009 */ |
0756eb3c PH |
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 */ |