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