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
PAM_conv(int num_msg,pam_message_type ** msg,struct pam_response ** resp,void * appdata_ptr)49 static int PAM_conv(int num_msg, pam_message_type **msg, struct pam_response **resp, void *appdata_ptr)
50 {
51 int count;
52 struct pam_response *repl;
53 struct pam_data *pd = (struct pam_data *)appdata_ptr;
54
55 if (!(repl = calloc(num_msg, sizeof(struct pam_response)))) {
56 return PAM_CONV_ERR;
57 }
58
59 for (count = 0; count < num_msg; count++) {
60 switch (msg[count]->msg_style) {
61 case PAM_TEXT_INFO:
62 pd->conv(ConvPutInfo, msg[count]->msg);
63 break;
64 case PAM_ERROR_MSG:
65 pd->conv(ConvPutError, msg[count]->msg);
66 break;
67 default:
68 switch (msg[count]->msg_style) {
69 case PAM_PROMPT_ECHO_ON:
70 repl[count].resp = pd->conv(ConvGetNormal, msg[count]->msg);
71 break;
72 case PAM_PROMPT_ECHO_OFF:
73 repl[count].resp = pd->conv(ConvGetHidden, pd->classic ? 0 : msg[count]->msg);
74 break;
75 #ifdef PAM_BINARY_PROMPT
76 case PAM_BINARY_PROMPT:
77 repl[count].resp = pd->conv(ConvGetBinary, msg[count]->msg);
78 break;
79 #endif
80 default:
81 /* Must be an error of some sort... */
82 goto conv_err;
83 }
84 if (!repl[count].resp) {
85 pd->abort = 1;
86 goto conv_err;
87 }
88 repl[count].resp_retcode = PAM_SUCCESS;
89 break;
90 }
91 }
92 *resp = repl;
93 return PAM_SUCCESS;
94
95 conv_err:
96 for (; count >= 0; count--) {
97 if (repl[count].resp) {
98 switch (msg[count]->msg_style) {
99 case PAM_PROMPT_ECHO_OFF:
100 dispose(repl[count].resp);
101 break;
102 #ifdef PAM_BINARY_PROMPT
103 case PAM_BINARY_PROMPT: /* handle differently? */
104 #endif
105 case PAM_PROMPT_ECHO_ON:
106 free(repl[count].resp);
107 break;
108 }
109 }
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
fail_delay(int retval ATTR_UNUSED,unsigned usec_delay ATTR_UNUSED,void * appdata_ptr ATTR_UNUSED)123 static void fail_delay(int retval ATTR_UNUSED, unsigned usec_delay ATTR_UNUSED, void *appdata_ptr ATTR_UNUSED)
124 {
125 }
126 #endif
127
Authenticate(const char * method,const char * user,char * (* conv)(ConvRequest,const char *))128 AuthReturn Authenticate(const char *method, const char *user, char *(*conv)(ConvRequest, const char *))
129 {
130 const char *tty;
131 pam_handle_t *pamh;
132 pam_gi_type pam_item;
133 const char *pam_service;
134 char pservb[64];
135 int pam_error;
136
137 openlog("kcheckpass", LOG_PID, LOG_AUTH);
138
139 PAM_data.conv = conv;
140 if (strcmp(method, "classic")) {
141 sprintf(pservb, "%.31s-%.31s", KSCREENSAVER_PAM_SERVICE, method);
142 pam_service = pservb;
143 } else {
144 /* PAM_data.classic = 1; */
145 pam_service = KSCREENSAVER_PAM_SERVICE;
146 }
147 pam_error = pam_start(pam_service, user, &PAM_conversation, &pamh);
148 if (pam_error != PAM_SUCCESS) {
149 return AuthError;
150 }
151
152 tty = ttyname(0);
153 if (!tty) {
154 tty = getenv("DISPLAY");
155 }
156
157 pam_error = pam_set_item(pamh, PAM_TTY, tty);
158 if (pam_error != PAM_SUCCESS) {
159 pam_end(pamh, pam_error);
160 return AuthError;
161 }
162
163 #ifdef PAM_FAIL_DELAY
164 pam_set_item(pamh, PAM_FAIL_DELAY, (void *)fail_delay);
165 #endif
166
167 pam_error = pam_authenticate(pamh, 0);
168 if (pam_error != PAM_SUCCESS) {
169 if (PAM_data.abort) {
170 PAM_data.abort = 0;
171 pam_end(pamh, pam_error);
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