1 #ifdef __cplusplus
2 extern "C" {
3 #endif
4 #include "EXTERN.h"
5 #include "perl.h"
6 #include "XSUB.h"
7 #ifdef __cplusplus
8 }
9 #endif
10
11 #include <PAM_config.h>
12
13 #if defined( HAVE_SECURITY_PAM_APPL_H )
14 # include <security/pam_appl.h>
15 #else
16 # if defined( HAVE_PAM_PAM_APPL_H )
17 # include <pam/pam_appl.h>
18 # endif
19 #endif
20
21 /*
22 Description of the macros used by this file.
23
24 | If your PAM library has the pam_get/putenv functions (PAM versions
25 | after 0.54) the following macro should be defined.
26 |
27 #define HAVE_PAM_GETENV
28
29 | The following macro activates a workaround for a bug in the solaris 2.6
30 | PAM library by setting a pointer to the perl conversation function
31 | before every call to a pam function
32 |
33 #define STATIC_CONV_FUNC
34 */
35
36
37 /* this is now determined from configure script */
38
39
40 #if defined( sun ) || defined( __hpux )
41
42 #define CONST_VOID void
43 #define CONST_STRUCT struct
44
45 #else
46
47 #define CONST_STRUCT const struct
48 #define CONST_VOID const void
49
50 #endif
51
52 struct perl_pam_data {
53 SV* conv_func;
54 SV* delay_func;
55 };
56
57 typedef struct pam_conv sPamConv;
58 typedef struct pam_response sPamResponse;
59 typedef struct perl_pam_data sPerlPamData;
60
61 /*
62 * Gets conv_struct->appdata_ptr and casts it as a sPerlPamData
63 */
64 static sPerlPamData*
get_perl_pam_data(pamh)65 get_perl_pam_data(pamh)
66 pam_handle_t *pamh;
67 {
68 int res;
69 sPamConv *cs;
70 res = pam_get_item(pamh, PAM_CONV, (CONST_VOID **)&cs);
71 if (res != PAM_SUCCESS || cs == NULL || cs->appdata_ptr == NULL)
72 croak("Error in getting pam data!");
73 else
74 return (sPerlPamData*)cs->appdata_ptr;
75 }
76
77
78 #ifdef STATIC_CONV_FUNC
79
80 static sPerlPamData *static_perl_pam_data = NULL;
81
82 #define SET_CONV_FUNC(pamh) static_perl_pam_data = get_perl_pam_data(pamh)
83
84 #else
85
86 #define SET_CONV_FUNC(pamh)
87
88 #endif
89
90 static int
not_here(s)91 not_here(s)
92 char *s;
93 {
94 croak("%s not implemented on this architecture", s);
95 return -1;
96 }
97
98
99 static int
my_conv_func(num_msg,msg,resp,appdata_ptr)100 my_conv_func(num_msg, msg, resp, appdata_ptr)
101 int num_msg;
102 CONST_STRUCT pam_message **msg;
103 sPamResponse **resp;
104 void *appdata_ptr;
105 {
106 int i,res_cnt,res;
107 STRLEN len;
108 sPamResponse *reply = NULL;
109 SV *strSV;
110 char *str;
111 dSP;
112
113 ENTER;
114 SAVETMPS;
115
116 PUSHMARK(sp);
117 for (i = 0; i < num_msg; i++) {
118 #ifdef sun
119 XPUSHs(sv_2mortal(newSViv((*msg)[i].msg_style)));
120 XPUSHs(sv_2mortal(newSVpv((*msg)[i].msg, 0)));
121 #else
122 XPUSHs(sv_2mortal(newSViv((msg[i])->msg_style)));
123 XPUSHs(sv_2mortal(newSVpv((msg[i])->msg, 0)));
124 #endif
125 }
126 PUTBACK;
127
128 #ifdef STATIC_CONV_FUNC
129 appdata_ptr = static_perl_pam_data;
130 #endif
131 if ( !SvTRUE(((sPerlPamData*)appdata_ptr)->conv_func) )
132 croak("Calling empty conversation function!");
133 res_cnt =
134 perl_call_sv(((sPerlPamData*)appdata_ptr)->conv_func, G_ARRAY);
135
136 SPAGAIN;
137
138 if (res_cnt == 1) { // only return code
139 res = POPi;
140 reply = NULL;
141 }
142 else if (res_cnt == 2*num_msg + 1) {
143 res = POPi;
144 res_cnt--;
145 if (res_cnt > 0) {
146 res_cnt /= 2;
147 reply = malloc( res_cnt * sizeof(sPamResponse));
148 for (i = res_cnt - 1; i >= 0; i--) {
149 strSV = POPs;
150 str = SvPV(strSV, len);
151 reply[i].resp_retcode = POPi;
152 reply[i].resp = malloc(len+1);
153 memcpy(reply[i].resp, str, len);
154 reply[i].resp[len] = 0;
155 /*
156 printf("Code %d and str %s\n", reply[i].resp_retcode,
157 reply[i].resp);
158 */
159 }
160 }
161 }
162 else {
163 croak("The output list of the PAM conversation function"
164 " must have twice the size of the input list plus one!");
165 res = PAM_CONV_ERR;
166 }
167
168 PUTBACK;
169
170 FREETMPS;
171 LEAVE;
172
173 *resp = reply;
174 return res;
175 }
176
177
178 /*
179 * We must also handle setting a delay function with a prototype:
180 *
181 * void (*delay_fn)(int retval, unsigned usec_delay, void *appdata_ptr);
182 *
183 * by a call to pam_set_item(pamh, PAM_FAIL_DELAY, fail_delay);
184 *
185 * Works only on Linux-PAM >= 0.68
186 */
187 static void
my_delay_func(status,delay,appdata_ptr)188 my_delay_func(status, delay, appdata_ptr)
189 int status;
190 unsigned int delay;
191 void *appdata_ptr;
192 {
193 dSP ;
194
195 if (appdata_ptr == NULL)
196 croak("Empty perl pam data");
197 if (!SvTRUE(((sPerlPamData*)appdata_ptr)->delay_func))
198 croak("Calling empty delay function!");
199
200 /* printf("st: %d, dl: %d\n",status,delay); */
201
202 PUSHMARK(sp) ;
203 XPUSHs(sv_2mortal(newSViv(status)));
204 XPUSHs(sv_2mortal(newSViv(delay)));
205 PUTBACK ;
206
207 perl_call_sv(((sPerlPamData*)appdata_ptr)->delay_func,
208 G_VOID | G_DISCARD);
209 }
210
211 static double
constant(name,arg)212 constant(name, arg)
213 char *name;
214 int arg;
215 {
216 errno = 0;
217
218 if (strncmp(name, "PAM_", 4) == 0) {
219 name = &name[4];
220 /* error codes */
221 if (strcmp(name, "SUCCESS") == 0)
222 return PAM_SUCCESS;
223 else if (strcmp(name, "OPEN_ERR") == 0)
224 return PAM_OPEN_ERR;
225 else if (strcmp(name, "SYMBOL_ERR") == 0)
226 return PAM_SYMBOL_ERR;
227 else if (strcmp(name, "SERVICE_ERR") == 0)
228 return PAM_SERVICE_ERR;
229 else if (strcmp(name, "SYSTEM_ERR") == 0)
230 return PAM_SYSTEM_ERR;
231 else if (strcmp(name, "BUF_ERR") == 0)
232 return PAM_BUF_ERR;
233 else if (strcmp(name, "PERM_DENIED") == 0)
234 return PAM_PERM_DENIED;
235 else if (strcmp(name, "AUTH_ERR") == 0)
236 return PAM_AUTH_ERR;
237 else if (strcmp(name, "CRED_INSUFFICIENT") == 0)
238 return PAM_CRED_INSUFFICIENT;
239 else if (strcmp(name, "AUTHINFO_UNAVAIL") == 0)
240 return PAM_AUTHINFO_UNAVAIL;
241 else if (strcmp(name, "USER_UNKNOWN") == 0)
242 return PAM_USER_UNKNOWN;
243 else if (strcmp(name, "MAXTRIES") == 0)
244 return PAM_MAXTRIES;
245 else if (strcmp(name, "NEW_AUTHTOK_REQD") == 0 ||
246 strcmp(name, "AUTHTOKEN_REQD") == 0)
247 #if defined(HAVE_PAM_NEW_AUTHTOK_REQD)
248 return PAM_NEW_AUTHTOK_REQD;
249 #elif defined(HAVE_PAM_AUTHTOKEN_REQD)
250 return PAM_AUTHTOKEN_REQD; /* Old Linux-PAM */
251 #else
252 goto not_there;
253 #endif
254 else if (strcmp(name, "ACCT_EXPIRED") == 0)
255 return PAM_ACCT_EXPIRED;
256 else if (strcmp(name, "SESSION_ERR") == 0)
257 return PAM_SESSION_ERR;
258 else if (strcmp(name, "CRED_UNAVAIL") == 0)
259 return PAM_CRED_UNAVAIL;
260 else if (strcmp(name, "CRED_EXPIRED") == 0)
261 return PAM_CRED_EXPIRED;
262 else if (strcmp(name, "CRED_ERR") == 0)
263 return PAM_CRED_ERR;
264 else if (strcmp(name, "NO_MODULE_DATA") == 0)
265 return PAM_NO_MODULE_DATA;
266 else if (strcmp(name, "CONV_ERR") == 0)
267 return PAM_CONV_ERR;
268 else if (strcmp(name, "AUTHTOK_ERR") == 0)
269 return PAM_AUTHTOK_ERR;
270 else if (strcmp(name, "AUTHTOK_RECOVER_ERR") == 0 ||
271 strcmp(name, "AUTHTOK_RECOVERY_ERR") == 0)
272 #if defined(HAVE_PAM_AUTHTOK_RECOVER_ERR) /* Linux-PAM */
273 return PAM_AUTHTOK_RECOVER_ERR;
274 #elif defined(HAVE_PAM_AUTHTOK_RECOVERY_ERR) /* Solaris PAM */
275 return PAM_AUTHTOK_RECOVERY_ERR;
276 #else
277 goto not_there;
278 #endif
279 else if (strcmp(name, "AUTHTOK_LOCK_BUSY") == 0)
280 return PAM_AUTHTOK_LOCK_BUSY;
281 else if (strcmp(name, "AUTHTOK_DISABLE_AGING") == 0)
282 return PAM_AUTHTOK_DISABLE_AGING;
283 else if (strcmp(name, "TRY_AGAIN") == 0)
284 return PAM_TRY_AGAIN;
285 else if (strcmp(name, "IGNORE") == 0)
286 return PAM_IGNORE;
287 else if (strcmp(name, "ABORT") == 0)
288 return PAM_ABORT;
289 else if (strcmp(name, "AUTHTOK_EXPIRED") == 0)
290 #if defined(HAVE_PAM_AUTHTOK_EXPIRED)
291 return PAM_AUTHTOK_EXPIRED;
292 #else
293 goto not_there;
294 #endif
295 else if (strcmp(name, "MODULE_UNKNOWN") == 0)
296 #if defined(HAVE_PAM_MODULE_UNKNOWN) /* Linux-PAM only */
297 return PAM_MODULE_UNKNOWN;
298 #else
299 goto not_there;
300 #endif
301 else if (strcmp(name, "BAD_ITEM") == 0)
302 #if defined(HAVE_PAM_BAD_ITEM)
303 return PAM_BAD_ITEM;
304 #else
305 goto not_there;
306 #endif
307
308 /* New Linux-PAM return codes */
309 else if (strcmp(name, "CONV_AGAIN") == 0)
310 #if defined(HAVE_PAM_CONV_AGAIN)
311 return PAM_CONV_AGAIN;
312 #else
313 goto not_there;
314 #endif
315 else if (strcmp(name, "INCOMPLETE") == 0)
316 #if defined(HAVE_PAM_INCOMPLETE)
317 return PAM_INCOMPLETE;
318 #else
319 goto not_there;
320 #endif
321
322 /* set/get_item constants */
323 else if (strcmp(name, "SERVICE") == 0)
324 return PAM_SERVICE;
325 else if (strcmp(name, "USER") == 0)
326 return PAM_USER;
327 else if (strcmp(name, "TTY") == 0)
328 return PAM_TTY;
329 else if (strcmp(name, "RHOST") == 0)
330 return PAM_RHOST;
331 else if (strcmp(name, "CONV") == 0)
332 return PAM_CONV;
333 /* module flags */
334 /*
335 else if (strcmp(name, "AUTHTOK") == 0)
336 return PAM_CONV;
337 else if (strcmp(name, "OLDAUTHTOK") == 0)
338 return PAM_CONV;
339 */
340 else if (strcmp(name, "RUSER") == 0)
341 return PAM_RUSER;
342 else if (strcmp(name, "USER_PROMPT") == 0)
343 return PAM_USER_PROMPT;
344 else if (strcmp(name, "FAIL_DELAY") == 0)
345 #if defined(HAVE_PAM_FAIL_DELAY)
346 return PAM_FAIL_DELAY;
347 #else
348 goto not_there;
349 #endif
350
351 /* global flag */
352 else if (strcmp(name, "SILENT") == 0)
353 return PAM_SILENT;
354 /* pam_authenticate falgs */
355 else if (strcmp(name, "DISALLOW_NULL_AUTHTOK") == 0)
356 return PAM_DISALLOW_NULL_AUTHTOK;
357 /* pam_set_cred flags */
358 else if (strcmp(name, "ESTABLISH_CRED") == 0 ||
359 strcmp(name, "CRED_ESTABLISH") == 0)
360 #if defined(HAVE_PAM_ESTABLISH_CRED)
361 return PAM_ESTABLISH_CRED;
362 #elif defined(HAVE_PAM_CRED_ESTABLISH) /* Old Linux-PAM */
363 return PAM_CRED_ESTABLISH;
364 #else
365 goto not_there;
366 #endif
367 else if (strcmp(name, "DELETE_CRED") == 0 ||
368 strcmp(name, "CRED_DELETE") == 0)
369 #if defined(HAVE_PAM_DELETE_CRED)
370 return PAM_DELETE_CRED;
371 #elif defined(HAVE_PAM_CRED_DELETE) /* Old Linux-PAM */
372 return PAM_CRED_DELETE;
373 #else
374 goto not_there;
375 #endif
376 else if (strcmp(name, "REINITIALIZE_CRED") == 0 ||
377 strcmp(name, "CRED_REINITIALIZE") == 0)
378 #if defined(HAVE_PAM_REINITIALIZE_CRED)
379 return PAM_REINITIALIZE_CRED;
380 #elif defined(HAVE_PAM_CRED_REINITIALIZE)
381 return PAM_CRED_REINITIALIZE; /* Old Linux-PAM */
382 #else
383 goto not_there;
384 #endif
385 else if (strcmp(name, "REFRESH_CRED") == 0 ||
386 strcmp(name, "CRED_REFRESH") == 0)
387 #if defined(HAVE_PAM_REFRESH_CRED)
388 return PAM_REFRESH_CRED;
389 #elif defined(HAVE_PAM_CRED_REFRESH)
390 return PAM_CRED_REFRESH; /* Old Linux-PAM */
391 #else
392 goto not_there;
393 #endif
394 /* pam_chauthtok flags */
395 else if (strcmp(name, "CHANGE_EXPIRED_AUTHTOK") == 0)
396 return PAM_CHANGE_EXPIRED_AUTHTOK;
397
398 /* message style constants */
399 else if (strcmp(name, "PROMPT_ECHO_OFF") == 0)
400 return PAM_PROMPT_ECHO_OFF;
401 else if (strcmp(name, "PROMPT_ECHO_ON") == 0)
402 return PAM_PROMPT_ECHO_ON;
403 else if (strcmp(name, "ERROR_MSG") == 0)
404 return PAM_ERROR_MSG;
405 else if (strcmp(name, "TEXT_INFO") == 0)
406 return PAM_TEXT_INFO;
407 else if (strcmp(name, "RADIO_TYPE") == 0)
408 #if defined(HAVE_PAM_RADIO_TYPE)
409 return PAM_RADIO_TYPE;
410 #else
411 goto not_there;
412 #endif
413 else if (strcmp(name, "BINARY_PROMPT") == 0)
414 #if defined(HAVE_PAM_BINARY_PROMPT)
415 return PAM_BINARY_PROMPT;
416 #else
417 goto not_there;
418 #endif
419
420 /* I'm not sure if these are really needed... */
421 /*
422 else if (strcmp(name, "MAX_MSG_SIZE") == 0)
423 return PAM_MAX_MSG_SIZE;
424 else if (strcmp(name, "MAX_RESP_SIZE") == 0)
425 return PAM_MAX_RESP_SIZE;
426 */
427 }
428 else if (strncmp(name, "HAVE_PAM_", 9) == 0) {
429 name = &name[9];
430
431 if (strcmp(name, "FAIL_DELAY") == 0)
432 #if defined(HAVE_PAM_FAIL_DELAY)
433 return 1;
434 #else
435 return 0;
436 #endif
437 else if (strcmp(name, "ENV_FUNCTIONS") == 0)
438 #if defined(HAVE_PAM_GETENV)
439 return 1;
440 #else
441 return 0;
442 #endif
443 /*
444 else if (strcmp(name, "HAVE_PAM_SYSTEM_LOG") == 0)
445 #if defined(HAVE_PAM_SYSTEM_LOG)
446 return 1;
447 #else
448 return 0;
449 #endif
450 */
451 }
452
453 errno = EINVAL;
454 return 0;
455
456 not_there:
457 errno = ENOSYS;
458 return 0;
459 }
460
461
462 MODULE = Authen::PAM PACKAGE = Authen::PAM
463
464 PROTOTYPES: ENABLE
465
466
467 double
468 constant(name,arg)
469 char *name
470 int arg
471
472
473 int
474 _pam_start(service_name, user_sv, func, pamh)
475 const char *service_name
476 SV *user_sv
477 SV *func
478 pam_handle_t *pamh = NO_INIT
479 PREINIT:
480 sPamConv conv_st;
481 const char *user;
482 CODE:
483 user = SvOK(user_sv) ? SvPV_nolen(user_sv) : NULL;
484
485 conv_st.conv = my_conv_func;
486 conv_st.appdata_ptr = malloc(sizeof(sPerlPamData));
487 ((sPerlPamData*)conv_st.appdata_ptr)->conv_func = newSVsv(func);
488 ((sPerlPamData*)conv_st.appdata_ptr)->delay_func = newSViv(0);
489
490 RETVAL = pam_start(service_name, user, &conv_st, &pamh);
491 OUTPUT:
492 pamh
493 RETVAL
494
495 int
496 pam_end(pamh, pam_status=PAM_SUCCESS)
497 pam_handle_t *pamh
498 int pam_status
499 PREINIT:
500 sPerlPamData *data;
501 int res;
502 CODE:
503 data = get_perl_pam_data(pamh);
504 SvREFCNT_dec(data->conv_func);
505 SvREFCNT_dec(data->delay_func);
506 free(data);
507
508 RETVAL = pam_end(pamh, pam_status);
509 OUTPUT:
510 RETVAL
511
512 int
pam_set_item(pamh,item_type,item)513 pam_set_item(pamh, item_type, item)
514 pam_handle_t *pamh
515 int item_type
516 SV *item
517 PREINIT:
518 sPerlPamData *data;
519 int res;
520 CODE:
521 if (item_type == PAM_CONV) {
522 data = get_perl_pam_data(pamh);
523 sv_setsv(data->conv_func, item);
524 RETVAL = PAM_SUCCESS;
525 }
526 #if defined(HAVE_PAM_FAIL_DELAY)
527 else if (item_type == PAM_FAIL_DELAY) {
528 data = get_perl_pam_data(pamh);
529 sv_setsv(data->delay_func, item);
530 if (SvTRUE(item))
531 RETVAL = pam_set_item( pamh, item_type, my_delay_func);
532 else
533 RETVAL = pam_set_item( pamh, item_type, NULL);
534 }
535 #endif
536 else
537 #if (PERL_API_REVISION == 5 && PERL_API_VERSION >= 5)
538 RETVAL = pam_set_item( pamh, item_type, SvPV_nolen(item));
539 #else
540 RETVAL = pam_set_item( pamh, item_type, SvPV(item,na));
541 #endif
542 OUTPUT:
543 RETVAL
544
545 int
pam_get_item(pamh,item_type,item)546 pam_get_item(pamh, item_type, item)
547 pam_handle_t *pamh
548 int item_type
549 SV *item
550 PREINIT:
551 char *c;
552 sPerlPamData *data;
553 int res;
554 CODE:
555 if (item_type == PAM_CONV) {
556 data = get_perl_pam_data(pamh);
557 sv_setsv(item, data->conv_func);
558 RETVAL = PAM_SUCCESS;
559 }
560 #if defined(HAVE_PAM_FAIL_DELAY)
561 else if (item_type == PAM_FAIL_DELAY) {
562 data = get_perl_pam_data(pamh);
563 sv_setsv(item, data->delay_func);
564 RETVAL = PAM_SUCCESS;
565 }
566 #endif
567 else {
568 RETVAL = pam_get_item( pamh, item_type, (CONST_VOID **)&c);
569 sv_setpv(item, c);
570 }
571 OUTPUT:
572 item
573 RETVAL
574
575 const char *
576 pam_strerror(pamh, errnum)
577 pam_handle_t * pamh
578 int errnum
579 CODE:
580 #if defined(PAM_STRERROR_NEEDS_PAMH)
581 RETVAL = pam_strerror(pamh, errnum);
582 #else
583 RETVAL = pam_strerror(errnum);
584 #endif
585 OUTPUT:
586 RETVAL
587
588 #if defined(HAVE_PAM_GETENV)
589 int
590 pam_putenv(pamh, name_value)
591 pam_handle_t *pamh
592 const char *name_value
593 CODE:
594 RETVAL = pam_putenv(pamh, name_value);
595 OUTPUT:
596 RETVAL
597
598 const char *
599 pam_getenv(pamh, name)
600 pam_handle_t *pamh
601 const char *name
602 CODE:
603 RETVAL = pam_getenv(pamh, name);
604 OUTPUT:
605 RETVAL
606
607 void
608 _pam_getenvlist(pamh)
609 pam_handle_t *pamh
610 PREINIT:
611 char **res;
612 int i;
613 int c;
614 PPCODE:
615 res = pam_getenvlist(pamh);
616 c = 0;
617 while (res[c] != 0)
618 c++;
619 EXTEND(sp, c);
620 for (i = 0; i < c; i++)
621 PUSHs(sv_2mortal(newSVpv(res[i],0)));
622
623 #else
624
625 int
626 pam_putenv(pamh, name_value)
627 pam_handle_t *pamh
628 const char *name_value
629 CODE:
630 not_here("pam_putenv");
631
632 const char *
633 pam_getenv(pamh, name)
634 pam_handle_t *pamh
635 const char *name
636 CODE:
637 not_here("pam_getenv");
638
639
640 void
641 _pam_getenvlist(pamh)
642 pam_handle_t *pamh
643 CODE:
644 not_here("pam_getenvlist");
645
646 #endif
647
648
649 #if defined(HAVE_PAM_FAIL_DELAY)
650
651 int
652 pam_fail_delay(pamh, musec_delay)
653 pam_handle_t *pamh
654 unsigned int musec_delay
655 CODE:
656 RETVAL = pam_fail_delay(pamh, musec_delay);
657 OUTPUT:
658 RETVAL
659
660 #else
661
662 void
663 pam_fail_delay(pamh, musec_delay)
664 pam_handle_t * pamh
665 unsigned int musec_delay
666 CODE:
667 not_here("pam_fail_delay");
668
669 #endif
670
671
672 int
673 pam_authenticate(pamh, flags=0)
674 pam_handle_t *pamh
675 int flags
676 CODE:
677 SET_CONV_FUNC(pamh);
678 RETVAL = pam_authenticate(pamh,flags);
679 OUTPUT:
680 RETVAL
681
682 int
683 pam_setcred(pamh, flags)
684 pam_handle_t *pamh
685 int flags
686 CODE:
687 SET_CONV_FUNC(pamh);
688 RETVAL = pam_setcred(pamh,flags);
689 OUTPUT:
690 RETVAL
691
692 int
693 pam_acct_mgmt(pamh, flags=0)
694 pam_handle_t *pamh
695 int flags
696 CODE:
697 SET_CONV_FUNC(pamh);
698 RETVAL = pam_acct_mgmt(pamh,flags);
699 OUTPUT:
700 RETVAL
701
702 int
703 pam_open_session(pamh, flags=0)
704 pam_handle_t *pamh
705 int flags
706 CODE:
707 SET_CONV_FUNC(pamh);
708 RETVAL = pam_open_session(pamh,flags);
709 OUTPUT:
710 RETVAL
711
712 int
713 pam_close_session(pamh, flags=0)
714 pam_handle_t *pamh
715 int flags
716 CODE:
717 SET_CONV_FUNC(pamh);
718 RETVAL = pam_close_session(pamh, flags);
719 OUTPUT:
720 RETVAL
721
722 int
723 pam_chauthtok(pamh, flags=0)
724 pam_handle_t *pamh
725 int flags
726 CODE:
727 SET_CONV_FUNC(pamh);
728 RETVAL = pam_chauthtok(pamh, flags);
729 OUTPUT:
730 RETVAL
731