~ubuntu-branches/ubuntu/maverick/thinkfinger/maverick

« back to all changes in this revision

Viewing changes to pam/pam_thinkfinger.c

  • Committer: Bazaar Package Importer
  • Author(s): Luca Capello
  • Date: 2007-09-15 14:44:23 UTC
  • Revision ID: james.westby@ubuntu.com-20070915144423-6m2api7re19o2wxo
Tags: upstream-0.3
ImportĀ upstreamĀ versionĀ 0.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*   ThinkFinger Pluggable Authentication Module
 
2
 *
 
3
 *   PAM module for libthinkfinger which is a driver for the UPEK/SGS Thomson
 
4
 *   Microelectronics fingerprint reader.
 
5
 *
 
6
 *   Copyright (C) 2007 Timo Hoenig <thoenig@suse.de>, <thoenig@nouse.net>
 
7
 *
 
8
 *   This program is free software; you can redistribute it and/or modify
 
9
 *   it under the terms of the GNU General Public License as published by
 
10
 *   the Free Software Foundation; either version 2 of the License, or
 
11
 *   (at your option) any later version.
 
12
 *
 
13
 *   This program is distributed in the hope that it will be useful,
 
14
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
 *   GNU General Public License for more details.
 
17
 *
 
18
 *   You should have received a copy of the GNU General Public License
 
19
 *   along with this program; if not, write to the
 
20
 *   Free Software Foundation, Inc.,
 
21
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
22
 */
 
23
 
 
24
#include <config.h>
 
25
 
 
26
#include <libthinkfinger.h>
 
27
#include <pam_thinkfinger-uinput.h>
 
28
 
 
29
#include <stdio.h>
 
30
#include <stdarg.h>
 
31
#include <termios.h>
 
32
#include <unistd.h>
 
33
#include <pthread.h>
 
34
#include <syslog.h>
 
35
#include <security/pam_modules.h>
 
36
#ifdef HAVE_OLD_PAM
 
37
#include "pam_thinkfinger-compat.h"
 
38
#else
 
39
#include <security/pam_ext.h>
 
40
#endif
 
41
 
 
42
#define MAX_PATH 256
 
43
 
 
44
#define PAM_SM_AUTH
 
45
 
 
46
volatile static int pam_tf_debug = 0;
 
47
 
 
48
typedef struct {
 
49
        libthinkfinger *tf;
 
50
        const char *user;
 
51
        pthread_t t_pam_prompt;
 
52
        pthread_t t_thinkfinger;
 
53
        int swipe_retval;
 
54
        int prompt_retval;
 
55
        int isatty;
 
56
        int uinput_fd;
 
57
        pam_handle_t *pamh;
 
58
} pam_thinkfinger_s;
 
59
 
 
60
static void pam_thinkfinger_log (const pam_thinkfinger_s *pam_thinkfinger, int type, const char *format, ...)
 
61
{
 
62
        char message[LINE_MAX];
 
63
        va_list ap;
 
64
 
 
65
        if (pam_tf_debug) {
 
66
                va_start (ap, format);
 
67
                vsnprintf (message, sizeof(message), format, ap);
 
68
                va_end(ap);
 
69
                pam_syslog (pam_thinkfinger->pamh, type, message);
 
70
        }
 
71
}
 
72
 
 
73
static void pam_thinkfinger_options (const pam_thinkfinger_s *pam_thinkfinger, int argc, const char **argv)
 
74
{
 
75
        int i;
 
76
 
 
77
        for (i = 0; i < argc; i++) {
 
78
                if (!strcmp(argv[i], "debug"))
 
79
                        pam_tf_debug = 1;
 
80
                else if (!strcmp(argv[i], " ") || !strcmp(argv[i], "\t"))
 
81
                        continue;
 
82
                else
 
83
                        pam_thinkfinger_log (pam_thinkfinger, LOG_INFO,
 
84
                                             "Option '%s' is not recognised or not yet supported.", *(argv+i));
 
85
        }
 
86
}
 
87
 
 
88
static int pam_thinkfinger_user_sanity_check (const pam_thinkfinger_s *pam_thinkfinger)
 
89
{
 
90
        const char *user = pam_thinkfinger->user;
 
91
        size_t len = strlen(user);
 
92
 
 
93
        return strstr(user, "../") || user[0] == '-' || user[len - 1] == '/';
 
94
}
 
95
 
 
96
static int pam_thinkfinger_user_bir_check (const pam_thinkfinger_s *pam_thinkfinger)
 
97
{
 
98
        int retval = -1;
 
99
        int fd;
 
100
        char bir_file[MAX_PATH];
 
101
 
 
102
        snprintf (bir_file, MAX_PATH-1, "%s/%s.bir", PAM_BIRDIR, pam_thinkfinger->user);
 
103
        fd = open (bir_file, O_RDONLY | O_NOFOLLOW);
 
104
        if (fd == -1) {
 
105
                pam_thinkfinger_log (pam_thinkfinger, LOG_ERR,
 
106
                                     "Could not open '%s/%s.bir': (%s).", PAM_BIRDIR, pam_thinkfinger->user, strerror (errno));
 
107
                goto out;
 
108
        }
 
109
 
 
110
        retval = 0;
 
111
        close (fd);
 
112
 
 
113
out:
 
114
        return retval;
 
115
}
 
116
 
 
117
static libthinkfinger_state pam_thinkfinger_verify (const pam_thinkfinger_s *pam_thinkfinger)
 
118
{
 
119
        libthinkfinger_state tf_state = TF_STATE_VERIFY_FAILED;
 
120
        char bir_file[MAX_PATH];
 
121
        int retry = 20;
 
122
 
 
123
        snprintf (bir_file, MAX_PATH, "%s/%s.bir", PAM_BIRDIR, pam_thinkfinger->user);
 
124
 
 
125
        if (pam_thinkfinger->tf == NULL)
 
126
                goto out;
 
127
 
 
128
        libthinkfinger_set_file (pam_thinkfinger->tf, bir_file);
 
129
        /* if the USB device is being removed while verification (e.g. suspend) retry */
 
130
        while ((tf_state = libthinkfinger_verify (pam_thinkfinger->tf)) == TF_RESULT_USB_ERROR && --retry > 0)
 
131
                usleep (250000);
 
132
 
 
133
        if (retry == 0 && tf_state == TF_STATE_USB_ERROR)
 
134
                pam_thinkfinger_log (pam_thinkfinger, LOG_WARNING, "USB device did not reappear in time");
 
135
out:
 
136
        return tf_state;
 
137
}
 
138
 
 
139
static void thinkfinger_thread (void *data)
 
140
{
 
141
        int ret;
 
142
        pam_thinkfinger_s *pam_thinkfinger = data;
 
143
        libthinkfinger_state tf_state;
 
144
 
 
145
        pam_thinkfinger_log (pam_thinkfinger, LOG_NOTICE, "%s called.", __FUNCTION__);
 
146
 
 
147
        tf_state = pam_thinkfinger_verify (pam_thinkfinger);
 
148
        if (tf_state == TF_RESULT_VERIFY_SUCCESS) {
 
149
                pam_thinkfinger->swipe_retval = PAM_SUCCESS;
 
150
                pam_thinkfinger_log (pam_thinkfinger, LOG_NOTICE,
 
151
                                    "User '%s' authenticated (biometric identification record matched).", pam_thinkfinger->user);
 
152
        } else if (tf_state == TF_RESULT_VERIFY_FAILED) {
 
153
                pam_thinkfinger->swipe_retval = PAM_AUTH_ERR;
 
154
                pam_thinkfinger_log (pam_thinkfinger, LOG_NOTICE,
 
155
                                     "User '%s' verification failed (biometric identification record not matched).",
 
156
                                     pam_thinkfinger->user);
 
157
        } else {
 
158
                pam_thinkfinger->swipe_retval = PAM_AUTH_ERR;
 
159
                pam_thinkfinger_log (pam_thinkfinger, LOG_NOTICE,
 
160
                                     "User '%s' verification failed (0x%x).", pam_thinkfinger->user, tf_state);
 
161
                goto out;
 
162
        }
 
163
 
 
164
        ret = uinput_cr (&pam_thinkfinger->uinput_fd);
 
165
        if (ret != 0)
 
166
                pam_thinkfinger_log (pam_thinkfinger, LOG_ERR,
 
167
                                     "Could not send carriage return via uinput: %s.", strerror (ret));
 
168
out:
 
169
        pam_thinkfinger_log (pam_thinkfinger, LOG_NOTICE,
 
170
                             "%s returning '%d': %s.", __FUNCTION__, pam_thinkfinger->swipe_retval,
 
171
                             pam_thinkfinger->swipe_retval ? pam_strerror (pam_thinkfinger->pamh, pam_thinkfinger->swipe_retval) : "success");
 
172
        pthread_exit (NULL);
 
173
}
 
174
 
 
175
static void pam_prompt_thread (void *data)
 
176
{
 
177
        pam_thinkfinger_s *pam_thinkfinger = data;
 
178
        char *resp;
 
179
 
 
180
        /* always returning from pam_prompt due to the CR sent by the keyboard or by uinput */
 
181
        pam_prompt (pam_thinkfinger->pamh, PAM_PROMPT_ECHO_OFF, &resp, "Password or swipe finger: ");
 
182
        pam_set_item (pam_thinkfinger->pamh, PAM_AUTHTOK, resp);
 
183
 
 
184
        /* ThinkFinger thread will return once we call libthinkfinger_free */
 
185
        if (pam_thinkfinger->tf != NULL)
 
186
                libthinkfinger_free (pam_thinkfinger->tf);
 
187
 
 
188
        pthread_exit (NULL);
 
189
}
 
190
 
 
191
static const char *handle_error (libthinkfinger_init_status init_status)
 
192
{
 
193
        const char *msg;
 
194
 
 
195
        switch (init_status) {
 
196
        case TF_INIT_NO_MEMORY:
 
197
                msg = "Not enough memory.";
 
198
                break;
 
199
        case TF_INIT_USB_DEVICE_NOT_FOUND:
 
200
                msg = "USB device not found.";
 
201
                break;
 
202
        case TF_INIT_USB_OPEN_FAILED:
 
203
                msg = "Could not open USB device.";
 
204
                break;
 
205
        case TF_INIT_USB_CLAIM_FAILED:
 
206
                msg = "Could not claim USB device.";
 
207
                break;
 
208
        case TF_INIT_USB_HELLO_FAILED:
 
209
                msg = "Sending HELLO failed.";
 
210
                break;
 
211
        case TF_INIT_UNDEFINED:
 
212
                msg = "Undefined error.";
 
213
                break;
 
214
        default:
 
215
                msg = "Unknown error.";
 
216
        }       
 
217
 
 
218
        return msg;
 
219
}
 
220
 
 
221
PAM_EXTERN
 
222
int pam_sm_authenticate (pam_handle_t *pamh, int flags, int argc, const char **argv)
 
223
{
 
224
        int ret;
 
225
        int retval = PAM_AUTH_ERR;
 
226
        pam_thinkfinger_s pam_thinkfinger;
 
227
        struct termios term_attr;
 
228
        libthinkfinger_init_status init_status;
 
229
 
 
230
        pam_thinkfinger.swipe_retval = PAM_SERVICE_ERR;
 
231
        pam_thinkfinger.pamh = pamh;
 
232
 
 
233
        pam_thinkfinger_options (&pam_thinkfinger, argc, argv);
 
234
        pam_thinkfinger_log (&pam_thinkfinger, LOG_INFO, "%s called.", __FUNCTION__);
 
235
 
 
236
        pam_thinkfinger.isatty = isatty (STDIN_FILENO);
 
237
        if (pam_thinkfinger.isatty == 1)
 
238
                tcgetattr (STDIN_FILENO, &term_attr);
 
239
 
 
240
        pam_get_user (pamh, &pam_thinkfinger.user, NULL);
 
241
        if (pam_thinkfinger_user_sanity_check (&pam_thinkfinger) || pam_thinkfinger_user_bir_check (&pam_thinkfinger) < 0) {
 
242
                pam_thinkfinger_log (&pam_thinkfinger, LOG_ERR, "User '%s' is unknown.", pam_thinkfinger.user);
 
243
                retval = PAM_USER_UNKNOWN;
 
244
                goto out;
 
245
        }
 
246
 
 
247
        ret = uinput_open (&pam_thinkfinger.uinput_fd);
 
248
        if (ret != 0) {
 
249
                pam_thinkfinger_log (&pam_thinkfinger, LOG_ERR, "Initializing uinput failed: %s.", strerror (ret));
 
250
                retval = PAM_AUTHINFO_UNAVAIL;
 
251
                goto out;
 
252
        }
 
253
 
 
254
        pam_thinkfinger.tf = libthinkfinger_new (&init_status);
 
255
        if (init_status != TF_INIT_SUCCESS) {
 
256
                pam_thinkfinger_log (&pam_thinkfinger, LOG_ERR, "Error: %s", handle_error (init_status));
 
257
                retval = PAM_AUTHINFO_UNAVAIL;
 
258
                goto out;
 
259
        }
 
260
 
 
261
        ret = pthread_create (&pam_thinkfinger.t_pam_prompt, NULL, (void *) &pam_prompt_thread, &pam_thinkfinger);
 
262
        if (ret != 0) {
 
263
                pam_thinkfinger_log (&pam_thinkfinger, LOG_ERR, "Error calling pthread_create (%s).", strerror (ret));
 
264
                goto out;
 
265
        }
 
266
        ret = pthread_create (&pam_thinkfinger.t_thinkfinger, NULL, (void *) &thinkfinger_thread, &pam_thinkfinger);
 
267
        if (ret != 0) {
 
268
                pam_thinkfinger_log (&pam_thinkfinger, LOG_ERR, "Error calling pthread_create (%s).", strerror (ret));
 
269
                goto out;
 
270
        }
 
271
        ret = pthread_join (pam_thinkfinger.t_thinkfinger, NULL);
 
272
        if (ret != 0) {
 
273
                pam_thinkfinger_log (&pam_thinkfinger, LOG_ERR, "Error calling pthread_join (%s).", strerror (ret));
 
274
                goto out;
 
275
        }
 
276
        ret = pthread_join (pam_thinkfinger.t_pam_prompt, NULL);
 
277
        if (ret != 0) {
 
278
                pam_thinkfinger_log (&pam_thinkfinger, LOG_ERR, "Error calling pthread_join (%s).", strerror (ret));
 
279
                goto out;
 
280
        }
 
281
 
 
282
        if (pam_thinkfinger.uinput_fd > 0)
 
283
                uinput_close (&pam_thinkfinger.uinput_fd);
 
284
        if (pam_thinkfinger.isatty == 1) {
 
285
                tcsetattr (STDIN_FILENO, TCSADRAIN, &term_attr);
 
286
        }
 
287
 
 
288
        if (pam_thinkfinger.swipe_retval == PAM_SUCCESS)
 
289
                retval = PAM_SUCCESS;
 
290
        else
 
291
                retval = PAM_AUTHINFO_UNAVAIL;
 
292
out:
 
293
        pam_thinkfinger_log (&pam_thinkfinger, LOG_INFO,
 
294
                             "%s returning '%d': %s.", __FUNCTION__, retval, retval ? pam_strerror (pamh, retval) : "success");
 
295
        return retval;
 
296
}
 
297
 
 
298
PAM_EXTERN
 
299
int pam_sm_setcred (pam_handle_t *pamh, int flags, int argc, const char **argv)
 
300
{
 
301
        return PAM_SUCCESS;
 
302
}
 
303
 
 
304
PAM_EXTERN
 
305
int pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
 
306
{
 
307
        return PAM_SUCCESS;
 
308
}
 
309
 
 
310
#ifdef PAM_STATIC
 
311
struct pam_module _pam_thinkfinger_modstruct = {
 
312
        "pam_thinkfinger",
 
313
        pam_sm_authenticate,
 
314
        pam_sm_setcred,
 
315
        NULL,
 
316
        NULL,
 
317
        NULL,
 
318
        pam_sm_chauthtok
 
319
};
 
320
#endif