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
35struct pam_data {
36 char *(*conv) (ConvRequest, const char *);
37 int abort:1;
38 int classic:1;
39};
40
41#ifdef PAM_MESSAGE_CONST
42typedef const struct pam_message pam_message_type;
43typedef const void *pam_gi_type;
44#else
45typedef struct pam_message pam_message_type;
46typedef void *pam_gi_type;
47#endif
48
49static int
50PAM_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
115static struct pam_data PAM_data;
116
117static struct pam_conv PAM_conversation = {
118 &PAM_conv,
119 &PAM_data
120};
121
122#ifdef PAM_FAIL_DELAY
123static void
124fail_delay(int retval ATTR_UNUSED, unsigned usec_delay ATTR_UNUSED,
125 void *appdata_ptr ATTR_UNUSED)
126{}
127#endif
128
129
130AuthReturn 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