1 | /* |
2 | * Copyright (C) 1998 Caldera, Inc. |
3 | * Copyright (C) 2003 Oswald Buddenhagen <ossi@kde.org> |
4 | * |
5 | * This program is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2 of the License, or (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public |
16 | * License along with this program; if not, write to the Free |
17 | * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | */ |
19 | |
20 | #include "kcheckpass.h" |
21 | |
22 | #ifdef HAVE_PAM |
23 | |
24 | #include <stdio.h> |
25 | #include <stdlib.h> |
26 | #include <string.h> |
27 | #include <syslog.h> |
28 | |
29 | #ifdef HAVE_PAM_PAM_APPL_H |
30 | #include <pam/pam_appl.h> |
31 | #else |
32 | #include <security/pam_appl.h> |
33 | #endif |
34 | |
35 | struct pam_data { |
36 | char *(*conv) (ConvRequest, const char *); |
37 | int abort:1; |
38 | int classic:1; |
39 | }; |
40 | |
41 | #ifdef PAM_MESSAGE_CONST |
42 | typedef const struct pam_message pam_message_type; |
43 | typedef const void *pam_gi_type; |
44 | #else |
45 | typedef struct pam_message pam_message_type; |
46 | typedef void *pam_gi_type; |
47 | #endif |
48 | |
49 | static int |
50 | PAM_conv (int num_msg, pam_message_type **msg, |
51 | struct pam_response **resp, |
52 | void *appdata_ptr) |
53 | { |
54 | int count; |
55 | struct pam_response *repl; |
56 | struct pam_data *pd = (struct pam_data *)appdata_ptr; |
57 | |
58 | if (!(repl = calloc(num_msg, sizeof(struct pam_response)))) |
59 | return PAM_CONV_ERR; |
60 | |
61 | for (count = 0; count < num_msg; count++) |
62 | switch (msg[count]->msg_style) { |
63 | case PAM_TEXT_INFO: |
64 | pd->conv(ConvPutInfo, msg[count]->msg); |
65 | break; |
66 | case PAM_ERROR_MSG: |
67 | pd->conv(ConvPutError, msg[count]->msg); |
68 | break; |
69 | default: |
70 | switch (msg[count]->msg_style) { |
71 | case PAM_PROMPT_ECHO_ON: |
72 | repl[count].resp = pd->conv(ConvGetNormal, msg[count]->msg); |
73 | break; |
74 | case PAM_PROMPT_ECHO_OFF: |
75 | repl[count].resp = |
76 | pd->conv(ConvGetHidden, pd->classic ? 0 : msg[count]->msg); |
77 | break; |
78 | #ifdef PAM_BINARY_PROMPT |
79 | case PAM_BINARY_PROMPT: |
80 | repl[count].resp = pd->conv(ConvGetBinary, msg[count]->msg); |
81 | break; |
82 | #endif |
83 | default: |
84 | /* Must be an error of some sort... */ |
85 | goto conv_err; |
86 | } |
87 | if (!repl[count].resp) { |
88 | pd->abort = 1; |
89 | goto conv_err; |
90 | } |
91 | repl[count].resp_retcode = PAM_SUCCESS; |
92 | break; |
93 | } |
94 | *resp = repl; |
95 | return PAM_SUCCESS; |
96 | |
97 | conv_err: |
98 | for (; count >= 0; count--) |
99 | if (repl[count].resp) |
100 | switch (msg[count]->msg_style) { |
101 | case PAM_PROMPT_ECHO_OFF: |
102 | dispose(repl[count].resp); |
103 | break; |
104 | #ifdef PAM_BINARY_PROMPT |
105 | case PAM_BINARY_PROMPT: /* handle differently? */ |
106 | #endif |
107 | case PAM_PROMPT_ECHO_ON: |
108 | free(repl[count].resp); |
109 | break; |
110 | } |
111 | free(repl); |
112 | return PAM_CONV_ERR; |
113 | } |
114 | |
115 | static struct pam_data PAM_data; |
116 | |
117 | static struct pam_conv PAM_conversation = { |
118 | &PAM_conv, |
119 | &PAM_data |
120 | }; |
121 | |
122 | #ifdef PAM_FAIL_DELAY |
123 | static void |
124 | fail_delay(int retval ATTR_UNUSED, unsigned usec_delay ATTR_UNUSED, |
125 | void *appdata_ptr ATTR_UNUSED) |
126 | {} |
127 | #endif |
128 | |
129 | |
130 | AuthReturn Authenticate(const char *caller, const char *method, |
131 | const char *user, char *(*conv) (ConvRequest, const char *)) |
132 | { |
133 | const char *tty; |
134 | pam_handle_t *pamh; |
135 | pam_gi_type pam_item; |
136 | const char *pam_service; |
137 | char pservb[64]; |
138 | int pam_error; |
139 | |
140 | openlog("kcheckpass" , LOG_PID, LOG_AUTH); |
141 | |
142 | PAM_data.conv = conv; |
143 | if (strcmp(method, "classic" )) { |
144 | sprintf(pservb, "%.31s-%.31s" , caller, method); |
145 | pam_service = pservb; |
146 | } else { |
147 | /* PAM_data.classic = 1; */ |
148 | pam_service = caller; |
149 | } |
150 | pam_error = pam_start(pam_service, user, &PAM_conversation, &pamh); |
151 | if (pam_error != PAM_SUCCESS) |
152 | return AuthError; |
153 | |
154 | tty = ttyname(0); |
155 | if (!tty) |
156 | tty = getenv ("DISPLAY" ); |
157 | |
158 | pam_error = pam_set_item (pamh, PAM_TTY, tty); |
159 | if (pam_error != PAM_SUCCESS) { |
160 | pam_end(pamh, pam_error); |
161 | return AuthError; |
162 | } |
163 | |
164 | # ifdef PAM_FAIL_DELAY |
165 | pam_set_item (pamh, PAM_FAIL_DELAY, (void *)fail_delay); |
166 | # endif |
167 | |
168 | pam_error = pam_authenticate(pamh, 0); |
169 | if (pam_error != PAM_SUCCESS) { |
170 | if (PAM_data.abort) { |
171 | pam_end(pamh, PAM_SUCCESS); |
172 | return AuthAbort; |
173 | } |
174 | pam_end(pamh, pam_error); |
175 | switch (pam_error) { |
176 | case PAM_USER_UNKNOWN: |
177 | case PAM_AUTH_ERR: |
178 | case PAM_MAXTRIES: /* should handle this better ... */ |
179 | case PAM_AUTHINFO_UNAVAIL: /* returned for unknown users ... bogus */ |
180 | return AuthBad; |
181 | default: |
182 | return AuthError; |
183 | } |
184 | } |
185 | |
186 | /* just in case some module is stupid enough to ignore a preset PAM_USER */ |
187 | pam_error = pam_get_item (pamh, PAM_USER, &pam_item); |
188 | if (pam_error != PAM_SUCCESS) { |
189 | pam_end(pamh, pam_error); |
190 | return AuthError; |
191 | } |
192 | if (strcmp((const char *)pam_item, user)) { |
193 | pam_end(pamh, PAM_SUCCESS); /* maybe use PAM_AUTH_ERR? */ |
194 | return AuthBad; |
195 | } |
196 | |
197 | pam_error = pam_setcred(pamh, PAM_REFRESH_CRED); |
198 | /* ignore errors on refresh credentials. If this did not work we use the old ones. */ |
199 | |
200 | pam_end(pamh, PAM_SUCCESS); |
201 | return AuthOk; |
202 | } |
203 | |
204 | #endif |
205 | |