summaryrefslogtreecommitdiff
blob: d9562c8ca8cdf8afc71b272b5fb3c5ba224c15a7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
--- pam_skey-1.1.5/INSTALL
+++ pam_skey/INSTALL
@@ -1,5 +1,39 @@
 $Id$
 
+Gentoo patch
+------------
+Most everything below still holds, though the libraries required are now
+those used by Gentoo.  Other S/Key libraries may work with a bit of
+tweaking.
+
+The options listed for the module below are no longer valid.  See the
+Gentoo patch section in README for details.
+
+The intended method for configuring PAM is by using the newer module
+specification, with a line like:
+
+auth	[success=done ignore=ignore auth_err=die default=bad] /lib/security/pam_skey.so
+
+This is a combination of the standard "sufficient" and "requisite"
+specifications:
+
+- If the module returns PAM_SUCCESS, we are authenticated and no other
+  modules should be tested.
+- If the module returns PAM_IGNORE, then the module didn't accept its
+  input as an S/Key response, and the next module should try using
+  the input (using the try_first_pass option).
+- If the module returns PAM_AUTH_ERR, then the module accepted an
+  S/Key input but it was invalid.  Do not try any more modules in the
+  stack; the user already chose S/Key authentication.
+- If the module returns any other code, it is a simple error in processing.
+  Set the error flag but try other modules, just in case.
+
+The module is intended to be placed before another authentication module,
+like pam_unix.so; if not, it should be placed before pam_deny.so.
+
+If the newer module specification is unavailable in your version of PAM,
+the "sufficient" specification will work.
+
 Required
 --------
 For building this package you will probably need original Wietse Venema's
--- pam_skey-1.1.5/Makefile.in
+++ pam_skey/Makefile.in
@@ -12,41 +12,26 @@
 LIBS=@LIBS@ @SKEYLIB@ @PAMLIB@
 LDFLAGS=@LDFLAGS@
 
-INSTALL=@INSTALL@ -m 644
+INSTALL=@INSTALL@
+INSTALL_LIB=${INSTALL} -m 755
 RM=@RM@ -f
 CP=@CP@ -f
 LN=@LN@ -s
 AWK=@AWK@
 
-PAM_FILES=pam_skey.so.1 pam_skey_access.so.1
+PAM_FILES=pam_skey.so
 
 all: $(PAM_FILES)
 
-pam_skey.so.1: pam_skey.o
-	$(CC) $(CFLAGS) -o $@ $< $(LIBS) $(LDFLAGS)
-
-pam_skey_access.so.1: pam_skey_access.o
+pam_skey.so: pam_skey.o
 	$(CC) $(CFLAGS) -o $@ $< $(LIBS) $(LDFLAGS)
 
 lint-pam_skey:
 	lclint $(CFLAGS) pam_skey.c
 
-lint-pam_skey_access:
-	lclint $(CFLAGS) pam_skey_access.c
-
-install:
-	@if test ! -d $(INSTALLDIR); then \
-		mkdir -p $(INSTALLDIR); \
-	fi
-	@for file in $(PAM_FILES); do \
-		if test ! -f "$(INSTALLDIR)/$$file"; then \
-			echo "Installing $$file in $(INSTALLDIR)"; \
-			$(INSTALL) "$$file" "$(INSTALLDIR)/$$file"; \
-			(cd $(INSTALLDIR) && $(LN) "$$file" `echo $$file | cut -d. -f1,2`); \
-		else \
-			echo "$$file exists - will not overwrite it"; \
-		fi \
-	done \
+install: all
+	$(INSTALL) -d $(INSTALLDIR)
+	$(INSTALL_LIB) $(PAM_FILES) $(INSTALLDIR)
 
 clean:
 	$(RM) a.out core *.so.1 *.o *.bak
--- pam_skey-1.1.5/README
+++ pam_skey/README
@@ -1,5 +1,77 @@
 $Id$
 
+Gentoo patch
+------------
+
+The Gentoo pam_skey patch changes the original module in a number of ways.
+The behavior of the module is changed to make it more consistent with the
+PAM design, and several changes were made throughout the code to make the
+module interact better with the skey library used by Gentoo.  Many of
+these changes will break pam_skey's compatibility with other systems and
+libraries, but this is, after all, the Gentoo patch.
+
+A (not necessarily) exhaustive list of the changes is as follows:
+- pam_skey_access.so is completely removed, since the Gentoo skey library
+  does not support the skey_access() call.
+- The pam_skey.so authentication code is completely rewritten.  The
+  original code contained many references to the standard I/O library
+  (writing to stderr, etc.), as well as inconsistent communication with
+  the PAM libraries.  Also, the authentication process is different, as
+  described below.
+- The options accepted by the pam_skey.so module are different, as
+  described below.
+
+Four options are accepted by the pam_skey.so module:
+  debug                  - This option turns on debug logging.
+  try_first_pass         - This option tells the module to first try using
+                           the authentication token passed from the
+			   previous module as an S/Key response, before
+			   informing the user of the challenge.  If the
+			   token is not valid, the module will proceed with
+			   the standard process of challenging the user
+			   and requesting a response, subject to the
+			   no_default_skey option below.
+  use_first_pass         - This option is identical to the try_first_pass
+                           option, except that if the token is not valid,
+			   it will return silently without challenging the
+			   user.
+  no_default_skey        - This flag changes the behavior of pam_skey.
+                           Instead of immediately challenging the user with
+			   an S/Key challenge, it will present the user with
+			   a standard "Password: " prompt.  If the user enters
+			   the password "s/key" (case insensitive), it will
+			   then challenge the user.  Any other input will
+			   cause the module to pass the given password to the
+			   next module in the authentication stack (usually
+			   pam_unix.so with the try_first_pass option).
+
+The exact behavior of pam_skey.so is detailed below:
+
+1. Retrieve username from PAM, possibly querying the user for it.
+2. If the user does not have any S/Key information, return PAM_IGNORE to
+   proceed to the next module in the stack.
+3. If *_first_pass is enabled, check the given authentication token to see
+   if it is a valid response to the current S/Key challenge.  If so,
+   return PAM_SUCCESS.
+ 3a. If the token is invalid and use_first_pass is enabled, return
+     PAM_IGNORE.
+4. If no_default_skey is enabled, issue a "Password: " prompt.
+ 4a. If the response is anything besides "s/key" (case insensitive),
+     store it as the authentication token and return PAM_IGNORE.
+5. Display the current S/Key challenge and request a response, with
+   input not echoed.  If no_default_skey is enabled, this will only be
+   an S/Key response request; otherwise, it will request either an
+   S/Key response or a system passsword.
+ 5a. If an empty response is given, request the S/Key response again,
+     this time with input echoed.
+ 5b. If the response is a valid S/Key response, return PAM_SUCCESS.
+     Otherwise, return PAM_AUTHERR.
+6. If the response is a valid S/Key response, return PAM_SUCCESS.
+7. Otherwise, if no_default_skey is enabled (the user specifically
+   requested "s/key" authentication), return PAM_AUTHERR.
+8. Otherwise, store the response as the authentication token and
+   return PAM_IGNORE.
+
 About
 -----
 This is complete pam_skey modul as interface to existing S/Key
--- pam_skey-1.1.5/autoconf/acconfig.h
+++ pam_skey/autoconf/acconfig.h
@@ -1,17 +1,2 @@
 /* Define if we can include both string.h and strings.h */
 #undef STRING_WITH_STRINGS
-
-/* Define if you have Linux */
-#undef LINUX
-
-/* Define if you have *BSD */
-#undef BSD
-
-/* Define if not missing skeyaccess() */
-#undef HAVE_SKEYACCESS
-
-/* Define if not missing skeyinfo() */
-#undef HAVE_SKEYINFO
-
-/* Define if you have skeylookup() instead of skeyinfo() */
-#undef HAVE_SKEYLOOKUP
--- pam_skey-1.1.5/autoconf/configure.in
+++ pam_skey/autoconf/configure.in
@@ -10,18 +10,6 @@
 AC_LANG_C
 AC_LANG_SAVE
 
-dnl Get system type
-AC_CANONICAL_HOST
-MYHOST=$host_os
-case "$host_os" in
-*linux*)
-  AC_DEFINE(LINUX)
-  ;;
-*bsd*)
-  AC_DEFINE(BSD)
-  ;;
-esac
-
 dnl Package information
 PACKAGE=pam_skey
 VERSION=1.1.5
@@ -65,13 +53,9 @@
 AC_ARG_WITH(skey-inc, [  --with-skey-inc=DIR     Directory containing skey include files], CFLAGS="${CFLAGS} -I${withval}")
 
 dnl Check for skey library
-AC_CHECK_LIB(socket, socket)
-AC_CHECK_LIB(nsl, gethostbyname)
+AC_CHECK_LIB(socket, socket, LIBS="${LIBS} -lsocket")
+AC_CHECK_LIB(nsl, gethostbyname, LIBS="${LIBS} -lnsl")
 AC_CHECK_LIB(skey, skeyverify, SKEYLIB="-lskey", AC_MSG_ERROR(skey library not found or unknown interface))
-AC_CHECK_LIB(skey, skeyaccess, AC_DEFINE(HAVE_SKEYACCESS))
-AC_CHECK_LIB(skey, skeyinfo, AC_DEFINE(HAVE_SKEYINFO),
-  AC_CHECK_LIB(skey, skeylookup, AC_DEFINE(HAVE_SKEYLOOKUP))
-)
 
 dnl Check against -G linker flag
 hold_ldflags=$LDFLAGS
--- pam_skey-1.1.5/pam_skey.c
+++ pam_skey/pam_skey.c
@@ -1,5 +1,6 @@
-/* 
- * (c) 2001 Dinko Korunic, kreator@srce.hr
+/*
+ * Rewrite (c) 2005 Dani Church, dani.church@gmail.com
+ * Original (c) 2001 Dinko Korunic, kreator@srce.hr
  *
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
@@ -33,23 +34,23 @@
 #include <pwd.h> 
 #include <sys/types.h>
 #include <syslog.h>
+#include <ctype.h>
 
 #define PAM_SM_AUTH
 
 #include <security/pam_appl.h>
 #include <security/pam_modules.h>
+#include <security/_pam_macros.h>
 
 #include "skey.h"
 #include "pam_skey.h"
 #include "misc.h"
 
-#if defined linux || defined BSD
-#define _PAM_CONST const
-#define _PAM_MSG_CAST
-#else
-#define _PAM_CONST
-#define _PAM_MSG_CAST (struct pam_message **)
-#endif
+#define LOGDEBUG(x) if (mod_opt & _MOD_DEBUG) { syslog x ;}
+#define QUERY_USERNAME NULL /* Use default username prompt */
+#define QUERY_PASSWORD "Password: "
+#define QUERY_RESPONSE_OR_PASSWORD "S/Key response or password: "
+#define QUERY_RESPONSE "S/Key response: "
 
 PAM_EXTERN int pam_sm_setcred (pam_handle_t *pamh, int flags,
   int argc, const char **argv)
@@ -57,243 +58,121 @@
   return PAM_SUCCESS;
 }
 
+/*
+ * The authentication module will return the following status codes:
+ * PAM_SUCCESS: Successful authentication via S/Key.
+ * PAM_IGNORE: The user doesn't have S/Key or doesn't want to use it.
+ *             Continue with the next module, using try_first_pass.
+ * PAM_AUTH_ERR: The user asked to use S/Key, but failed the authentication.
+ *               Don't try any more PAM modules.
+ * others: random errors, try next authentication method
+ */
+
 PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags,
   int argc, const char **argv)
 {
-  char challenge[CHALLENGE_MAXSIZE]; /* challenge to print in conv */
-  char msg_text[PAM_MAX_MSG_SIZE]; /* text for pam conv */
-  char *username = NULL; /* username spacer */
+  const char *challenge; /* challenge to print in conv */
+  const char *username = NULL; /* username spacer */
   char *response = NULL; /* response spacer */
-  struct skey skey; /* structure that contains skey information */
   int status; /* return status spacer */
   unsigned mod_opt = _MOD_NONE_ON; /* module options */
 
   /* Get module options */
   mod_getopt(&mod_opt, argc, argv);
 
-  /* Get username */
-  if (pam_get_user(pamh, (_PAM_CONST char **)&username, "login:")
-      != PAM_SUCCESS)
-  {
-    fprintf(stderr, "cannot determine username\n");
-    if (mod_opt & _MOD_DEBUG)
-      syslog(LOG_DEBUG, "cannot determine username");
-    return PAM_USER_UNKNOWN;
-  }
-
-  if (mod_opt & _MOD_DEBUG)
-    syslog(LOG_DEBUG, "got username %s", username);
-
-#ifdef HAVE_SKEYACCESS
-  /* Check S/Key access permissions - user, host and port. Also include
-   * sanity checks */
-  if (mod_opt & _MOD_ACCESS_CHECK)
-  {
-    char *host; /* points to host */
-    char *port; /* points to port */
-    struct passwd *pwuser; /* structure for getpw() */
-
-    /* Get host.. */
-    if (pam_get_item(pamh, PAM_RHOST, (_PAM_CONST void **)&host)
-        != PAM_SUCCESS)
-      host = NULL; /* couldn't get host */
-    /* ..and port */
-    if (pam_get_item(pamh, PAM_TTY, (_PAM_CONST void **)&port)
-        != PAM_SUCCESS)
-      port = NULL; /* couldn't get port */
-
-    if (mod_opt & _MOD_DEBUG)
-      syslog(LOG_DEBUG, "checking s/key access for user %s,"
-        " host %s, port %s", username,
-        (host != NULL) ? host : "*unknown*",
-        (port != NULL) ? port : "*unknown*");
-
-    /* Get information from passwd file */
-    if ((pwuser = getpwnam(username)) == NULL)
-    {
-      fprintf(stderr, "no such user\n");
-      syslog(LOG_NOTICE, "cannot find user %s", username);
-      return PAM_USER_UNKNOWN; /* perhaps even return PAM_ABORT here? */
+  /* Get username (taken mainly from pam_unix) */
+  status = pam_get_user(pamh, &username, QUERY_USERNAME);
+  if (status == PAM_SUCCESS) {
+    if (username == NULL || !isalnum(*username)) {
+      syslog(LOG_ERR, "bad username [%s]", username);
+      return PAM_USER_UNKNOWN;
     }
+    LOGDEBUG((LOG_DEBUG, "username [%s] obtained", username));
+  } else {
+    LOGDEBUG((LOG_DEBUG, "trouble reading username"));
+    if (status == PAM_CONV_AGAIN)
+      return PAM_INCOMPLETE;
+    return status;
+  }
 
-    /* Do actual checking - we assume skeyaccess() returns PERMIT which is
-     * by default 1. Notice 4th argument is NULL - we will not perform
-     * address checks on host itself */
-    if (skeyaccess(pwuser, port, host, NULL) != 1)
-    {
-      fprintf(stderr, "no s/key access permissions\n");
-      syslog(LOG_NOTICE, "no s/key access permissions for %s",
-          username);
-      return PAM_AUTH_ERR;
-    }
+  /* Check whether or not this user has an S/Key */
+  if (skey_haskey(username) != 0) {
+    LOGDEBUG((LOG_DEBUG, "user [%s] has no S/Key entry", username));
+    return PAM_IGNORE;
   }
-  else
 
-#endif /* HAVE_SKEYACCESS */
-    
-  /* Only do check whether user has passwd entry */
-    if (getpwnam(username) == NULL)
-    {
-      fprintf(stderr, "no such user\n");
-      if (mod_opt & _MOD_DEBUG)
-        syslog(LOG_DEBUG, "cannot find user %s",
-            username);
-      return PAM_USER_UNKNOWN;
+  if ((mod_opt & _MOD_TRY_FIRST_PASS) || (mod_opt & _MOD_USE_FIRST_PASS)) {
+    status = pam_get_item(pamh, PAM_AUTHTOK, (const void **)(void *)&response);
+    if (status != PAM_SUCCESS) {
+      syslog(LOG_ALERT, "pam_get_item returned error to pam_skey");
+      return status;
+    } else if (response != NULL) {
+      if (skey_passcheck(username, response) != -1) {
+	return PAM_SUCCESS;
+      } else if (mod_opt & _MOD_USE_FIRST_PASS) {
+	return PAM_IGNORE;
+      }
+    } else if (mod_opt & _MOD_USE_FIRST_PASS) {
+      return PAM_AUTHTOK_RECOVER_ERR;
     }
-
-  /* Get S/Key information on user with skeyinfo() */
-#ifdef HAVE_SKEYINFO
-  switch (skeyinfo(&skey, username, NULL))
-#else
-#ifdef HAVE_SKEYLOOKUP
-  switch (skeylookup(&skey, username))
-#endif /* HAVE_SKEYLOOKUP */
-#endif /* HAVE_SKEYINFO */
-  {
-  /* 0: OK */
-  case 0:
-    break;
-  /* -1: File error */
-  case -1:
-#if 0
-  /* XXX- This seems broken in (at least) logdaemon-5.8. It returns -1
-   * when user not found in keyfile. -kre */
-    fprintf(stderr, "s/key database error\n");
-    syslog(LOG_NOTICE, "s/key database error");
-    return PAM_AUTH_ERR;
-#endif
-  /* 1: No such user in database */
-  case 1:
-    /* We won't confuse the ordinary user telling him about missing skeys
-     * -kre */
-#if 0
-    fprintf(stderr, "no s/key for %s\n", username);
-#endif
-    if (mod_opt & _MOD_DEBUG)
-      syslog(LOG_DEBUG, "no s/key for %s\n", username);
-    return PAM_AUTH_ERR;
   }
 
-  /* Make challenge string */
-#if defined(SKEY_MAX_HASHNAME_LEN) && defined(SKEY_MAX_SEED_LEN)
-  snprintf(challenge, CHALLENGE_MAXSIZE, "otp-%.*s %d %.*s",
-      SKEY_MAX_HASHNAME_LEN, skey_get_algorithm(), skey.n - 1, SKEY_MAX_SEED_LEN, skey.seed);
-#else
-  snprintf(challenge, CHALLENGE_MAXSIZE, "s/key %d %s",
-      skey.n - 1, skey.seed);
-#endif
-
-  if (mod_opt & _MOD_DEBUG)
-    syslog(LOG_DEBUG, "got challenge %s for %s", challenge,
-        username);
-
-  /* Read response from last module's PAM_AUTHTOK */
-  if (mod_opt & _MOD_USE_FIRST_PASS)
-  {
-    /* Try to extract authtoken */
-    if (pam_get_item(pamh, PAM_AUTHTOK, (_PAM_CONST void **)&response)
-        != PAM_SUCCESS)
-    {
-      if (mod_opt & _MOD_DEBUG)
-        syslog(LOG_DEBUG, "could not get PAM_AUTHTOK");
-      mod_opt &= ~_MOD_USE_FIRST_PASS;
+  if (mod_opt & _MOD_NO_DEFAULT_SKEY) {
+    status = mod_talk_touser(pamh, mod_opt, NULL, QUERY_PASSWORD, 0, &response);
+    if (status != PAM_SUCCESS) {
+      _pam_delete(response)
+      return status;
     }
-    else
-    {
-      /* Got AUTHTOK, but it was empty */
-      if (empty_authtok(response))
-      {
-        if (mod_opt & _MOD_DEBUG)
-          syslog(LOG_DEBUG, "empty PAM_AUTHTOK");
-        mod_opt &= ~_MOD_USE_FIRST_PASS;
-      }
-      else
-        /* All OK, print challenge information */
-        fprintf(stderr, "challenge %s\n", challenge);
+    if (strcasecmp(response,"s/key")!=0) {
+      status = pam_set_item(pamh, PAM_AUTHTOK, response);
+      if (status != PAM_SUCCESS)
+	return status;
+      return PAM_IGNORE;
     }
+    _pam_delete(response);
   }
 
-  /* There was no PAM_AUTHTOK, or there was no such option in pam-conf
-   * file */
-  if (!(mod_opt & _MOD_USE_FIRST_PASS))
-  {
-    /* Prepare a complete message for conversation */
-    snprintf(msg_text, PAM_MAX_MSG_SIZE,
-        "challenge %s\npassword: ", challenge);
-
-    /* Talk with user */
-    if (mod_talk_touser(pamh, &mod_opt, msg_text, &response)
-        != PAM_SUCCESS)
-      return PAM_SERVICE_ERR;
-
-    /* Simulate standard S/Key login procedure - if empty token, turn on
-     * ECHO and prompt again */
-    if (empty_authtok(response) && !(mod_opt & _MOD_ONLY_ONE_TRY))
-    {
-      /* Was there echo off? */
-      if (mod_opt & _MOD_ECHO_OFF)
-      {
-        _pam_delete(response);
-        fprintf(stderr, "(turning echo on)\n");
-        mod_opt &= ~_MOD_ECHO_OFF;
-
-        /* Prepare a complete message for conversation */
-        snprintf(msg_text, PAM_MAX_MSG_SIZE, "password: ");
-
-        /* Talk with user */
-        if (mod_talk_touser(pamh, &mod_opt, msg_text, &response)
-          != PAM_SUCCESS)
-          return PAM_SERVICE_ERR;
-
-        /* Got again empty response. Bailout and don't save auth token */
-        if (empty_authtok(response))
-          return PAM_AUTH_ERR;
-      }
-      else
-      /* There was echo on already - just get out and don't save auth token
-       * for other modules */
-        return PAM_AUTH_ERR;
-    }
+  challenge = skey_keyinfo(username);
+  if (challenge == NULL) {
+    syslog(LOG_ALERT, "Could not retrieve S/Key challenge for [%s]", username);
+    return PAM_AUTHINFO_UNAVAIL;
+  }
 
-    /* XXX - ECHO ON puts '\n' at the end in Solaris 2.7! This is
-     * cludge to get rid of this nasty `feature' -kre */
-    _pam_degarbage(response);
-  
-    /* Store auth token - that next module can use with `use_first_pass' */
-    if (pam_set_item(pamh, PAM_AUTHTOK, response) != PAM_SUCCESS)
-    {
-      syslog(LOG_NOTICE, "unable to save auth token");
-      return PAM_SERVICE_ERR;
-    }
-  } 
+  if (mod_opt & _MOD_NO_DEFAULT_SKEY)
+    status = mod_talk_touser(pamh, mod_opt, challenge, QUERY_RESPONSE, 0, &response);
+  else
+    status = mod_talk_touser(pamh, mod_opt, challenge, QUERY_RESPONSE_OR_PASSWORD, 0, &response);
 
-  /* Verify S/Key */
-  status = skeyverify(&skey, response);
+  if (status != PAM_SUCCESS)
+    return status;
 
-  switch (status)
-  {
-    /* 0: Verify successful, database updated */
-    case 0:
-      break;
-    /* -1: Error of some sort; database unchanged */
-    /*  1: Verify failed, database unchanged */
-    case -1:
-    case 1:
-      if (mod_opt & _MOD_DEBUG)
-        syslog(LOG_DEBUG, "verify for %s failed, database"
-            " unchanged", username);
-
-      /* cleanup conversation (error occured) */
-      _pam_delete(response);
+  if (*response == '\0') {
+    _pam_delete(response);
+    status = mod_talk_touser(pamh, mod_opt, NULL, QUERY_RESPONSE, 1, &response);
+    if (status != PAM_SUCCESS)
+      return status;
+    status = pam_set_item(pamh, PAM_AUTHTOK, response);
+    status = skey_passcheck(username, response);
+    _pam_delete(response);
+    if (status != -1)
+      return PAM_SUCCESS;
+    return PAM_AUTH_ERR;
+  }
 
-      return PAM_AUTH_ERR;
+  status = pam_set_item(pamh, PAM_AUTHTOK, response);
+  status = skey_passcheck(username, response);
+  if (status != -1) {
+    _pam_delete(response);
+    return PAM_SUCCESS;
   }
 
-  /* cleanup conversation (it was valid) */
-  _pam_delete(response);
+  if (mod_opt & _MOD_NO_DEFAULT_SKEY) {
+    _pam_delete(response);
+    return PAM_AUTH_ERR;
+  }
 
-  /* Success by default */
-  return PAM_SUCCESS;
+  status = pam_set_item(pamh, PAM_AUTHTOK, response);
+  return PAM_IGNORE;
 }
 
 /* Get module optional parameters */
@@ -328,13 +207,15 @@
 }
 
 /* This will talk to user through PAM_CONV */
-static int mod_talk_touser(pam_handle_t *pamh, unsigned *mod_opt,
-    char *msg_text, char **response)
+static int mod_talk_touser(pam_handle_t *pamh, unsigned mod_opt,
+    const char *info_text, const char *prompt_text, int echo_on,
+    char **response)
 {
-  struct pam_message message;
-  const struct pam_message *pmessage = &message;
+  struct pam_message message[2], *pmessage[2];
   struct pam_conv *conv = NULL;
   struct pam_response *presponse = NULL;
+  int i=0;
+  int status;
 
   /* Better safe than sorry */
   *response = NULL;
@@ -342,40 +223,45 @@
   /* Be paranoid */
   memset(&message, 0, sizeof(message));
 
-  /* Turn on/off PAM echo */
-  if (*mod_opt & _MOD_ECHO_OFF)
-    message.msg_style = PAM_PROMPT_ECHO_OFF;
-  else
-    message.msg_style = PAM_PROMPT_ECHO_ON;
-  
-  /* Point to conversation text */
-  message.msg = msg_text;
+  pmessage[0] = &message[0];
+  pmessage[1] = &message[1];
 
-  /* Do conversation and see if all is OK */
-  if (pam_get_item(pamh, PAM_CONV, (_PAM_CONST void **)&conv)
+  /* Set info text, if any */
+  if (info_text) {
+    message[i].msg = info_text;
+    message[i].msg_style = PAM_TEXT_INFO;
+    i++;
+  }
+
+  /* Set prompt text */
+  message[i].msg = prompt_text;
+  message[i].msg_style = echo_on ? PAM_PROMPT_ECHO_ON : PAM_PROMPT_ECHO_OFF;
+  i++;
+
+  /* Get conversation function */
+  if (pam_get_item(pamh, PAM_CONV, (const void **)(void *)&conv)
       != PAM_SUCCESS)
   {
-    if (*mod_opt & _MOD_DEBUG)
-      syslog(LOG_DEBUG, "error in conversation");
+    LOGDEBUG((LOG_DEBUG, "error in conversation"));
     return PAM_SERVICE_ERR;
   }
-
-  /* Convert into pam_response - only 1 reply expected */
-  if (conv->conv(1, _PAM_MSG_CAST &pmessage, &presponse,
-        conv->appdata_ptr)
-    != PAM_SUCCESS)
+  /* Do conversation and see if all is OK */
+  status = conv->conv(i, (const struct pam_message **)pmessage,
+		      &presponse, conv->appdata_ptr);
+  if (status != PAM_SUCCESS)
   {
-    _pam_delete(presponse->resp);
-    return PAM_SERVICE_ERR;
+    if (presponse != NULL)
+      _pam_delete(presponse->resp);
+    return status;
   }
 
   if (presponse != NULL)
   {
     /* Save address */
-    *response = presponse->resp;
+    *response = presponse[i-1].resp;
     /* To ensure that response address will not be erased */
-    presponse->resp = NULL;
-    _pam_drop(presponse);
+    presponse[i-1].resp = NULL;
+    _pam_drop_reply(presponse,i);
   }
   else
     return PAM_SERVICE_ERR;
--- pam_skey-1.1.5/pam_skey.h
+++ pam_skey/pam_skey.h
@@ -22,29 +22,25 @@
  */
 
 /* Prototypes */
-#ifndef BSD
-extern int skeyinfo(struct skey *, char *, char *); /* ORGH! Not in skey.h */
-#endif
-
 static void mod_getopt(unsigned *, int, const char **);
-static int mod_talk_touser(pam_handle_t *, unsigned *, char *, char **);
+static int mod_talk_touser(pam_handle_t *, unsigned, const char *, const char *, int, char **);
 
 /* free() macro */
-#define _pam_drop(X)  \
+/*#define _pam_drop(X)  \
 if (X != NULL)        \
 {                     \
   free(X);            \
   X = NULL;           \
-}
+}*/
 
 /* Secure overwrite */
-#define _pam_overwrite(x)   \
+/*#define _pam_overwrite(x)   \
 {                           \
   register char *__xx__;    \
   if ((__xx__ = (x)))       \
     while (*__xx__)         \
     *__xx__++ = '\0';       \
-}
+}*/
 
 /* Drop-in secure replacement - we do not want cleartext passwords lying
  * scattered around */
@@ -56,7 +52,7 @@
 
 /* This will get us rid of first '\n' in response string and cut-off the
  * rest of the string. It should be ASCIIZ, of course */
-#define _pam_degarbage(x)      \
+/*#define _pam_degarbage(x)      \
 {                              \
   register char *__xx__;       \
     if ((__xx__ = (x)))        \
@@ -70,30 +66,25 @@
         else                   \
           __xx__++;            \
       }                        \
-}
+}*/
 
 /* Handy empty AUTHTOK macro */
 #define empty_authtok(a) (a == NULL || !*a || *a == '\n')
 
-/* Maximum challenge size. It should be 64, but be sure */
-#define CHALLENGE_MAXSIZE 128
-
 /* Define module flags */
-#define _MOD_NONE_ON        0x0000      /* Generic flag */
-#define _MOD_ALL_ON    (~_MOD_NONE_ON)  /* Generic mask */
-#define _MOD_DEBUG          0x0001      /* Debugging options on */
-#define _MOD_ECHO_OFF       0x0002      /* PAM_ECHO_OFF */
-#define _MOD_ACCESS_CHECK   0x0004      /* Check S/Key access permissions */
-#define _MOD_USE_FIRST_PASS 0x0008      /* Use PAM_AUTHTOK */
-#define _MOD_ONLY_ONE_TRY   0x0010      /* Only one try, no matter of echo */
-#define _MOD_SPACER         0x0020      /* Currently unused */
+#define _MOD_NONE_ON         0x0000	/* Generic flag */
+#define _MOD_ALL_ON    (~_MOD_NONE_ON)	/* Generic mask */
+#define _MOD_DEBUG           0x0001	/* Debugging options on */
+#define _MOD_TRY_FIRST_PASS  0x0002	/* Attempt using PAM_AUTHTOK */
+#define _MOD_USE_FIRST_PASS  0x0004	/* Only use PAM_AUTHTOK */
+#define _MOD_NO_DEFAULT_SKEY 0x0008	/* Don't use S/Key by default */
 
 /* Setup defaults - use echo off only */
-#define _MOD_DEFAULT_FLAG   _MOD_ECHO_OFF
+#define _MOD_DEFAULT_FLAG   _MOD_NONE_ON
 #define _MOD_DEFAULT_MASK   _MOD_ALL_ON
 
 /* Number of parameters currently known */
-#define _MOD_ARGS           8
+#define _MOD_ARGS           4
 
 /* Structure for flexible argument parsing */
 typedef struct
@@ -108,11 +99,7 @@
 {
   /* String            Mask                           Flag */
   {"debug",            _MOD_ALL_ON,                   _MOD_DEBUG},
-  {"echo=off",         _MOD_ALL_ON,                   _MOD_ECHO_OFF},
-  {"echo=on",          _MOD_ALL_ON^_MOD_ECHO_OFF,     _MOD_NONE_ON},
-  {"access_check=on",  _MOD_ALL_ON,                   _MOD_ACCESS_CHECK},
-  {"access_check=off", _MOD_ALL_ON^_MOD_ACCESS_CHECK, _MOD_NONE_ON},
+  {"try_first_pass",   _MOD_ALL_ON,                   _MOD_TRY_FIRST_PASS},
   {"use_first_pass",   _MOD_ALL_ON,                   _MOD_USE_FIRST_PASS},
-  {"try_first_pass",   _MOD_ALL_ON,                   _MOD_USE_FIRST_PASS},
-  {"only_one_try",     _MOD_ALL_ON,                   _MOD_ONLY_ONE_TRY}
+  {"no_default_skey",  _MOD_ALL_ON,                   _MOD_NO_DEFAULT_SKEY}
 };