-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathperl_helper.c
146 lines (117 loc) · 4.27 KB
/
perl_helper.c
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
#include <EXTERN.h>
#include <perl.h>
#include <security/pam_modules.h>
#include <syslog.h>
#include <xs_object_magic.h>
#include <assert.h>
#include "compat.h"
#include "perl_helper.h"
#define ORIGINAL_INTERPRETER_KEY "perl_original_interpreter"
#define MY_INTERPRETER_KEY "perl_my_interpreter"
EXTERN_C void xs_init(pTHX);
void cleanup_my_perl(pam_handle_t *pamh, void *data, int error_status);
int
invoke(const char *phase, pam_handle_t *pamh, int flags, int argc, const char **argv)
{
int rv = PAM_SYSTEM_ERR;
int i;
PerlInterpreter *original_interpreter, *my_perl;
pam_syslog(pamh, LOG_DEBUG, "Starting up");
if (pam_get_data(pamh, ORIGINAL_INTERPRETER_KEY, (void*)&original_interpreter) == PAM_SUCCESS) {
pam_syslog(pamh, LOG_DEBUG, "Unexpected, original interpreter is already defined");
return PAM_SYSTEM_ERR;
}
original_interpreter = PERL_GET_INTERP;
if (pam_get_data(pamh, MY_INTERPRETER_KEY, (void*)&my_perl) != PAM_SUCCESS) {
pam_syslog(pamh, LOG_DEBUG, "I don't have an interpreter allocated yet");
my_perl = NULL;
}
if (my_perl == NULL) {
int my_argc = 3;
char *my_argv[] = { "", "-T", "-e1", NULL }; // POSIX says it must be NULL terminated, even though we have argc
pam_syslog(pamh, LOG_DEBUG, "Creating a new perl interpreter");
if (original_interpreter == NULL) {
pam_syslog(pamh, LOG_DEBUG, "We're the first perl interpreter, initialize perl libs");
PERL_SYS_INIT(&my_argc, (char***)&my_argv);
}
my_perl = perl_alloc();
PERL_SET_CONTEXT(my_perl);
perl_construct(my_perl);
perl_parse(my_perl, xs_init, my_argc, my_argv, (char **)NULL);
}
else {
pam_syslog(pamh, LOG_DEBUG, "Already have a perl interpreter, change context to it");
PERL_SET_CONTEXT(my_perl);
}
if (argc < 1 || argv[0] == NULL) {
pam_syslog(pamh, LOG_DEBUG, "We were called with no arguments, don't know what to load");
return PAM_MODULE_UNKNOWN;
}
SV *module_name = newSVpv(argv[0], 0);
load_module(0, newSVsv(module_name), NULL, NULL);
SV *other_module_name = newSVpv("XS::Object::Magic", 0);
load_module(0, newSVsv(other_module_name), NULL, NULL);
SV *pamh_sv = xs_object_magic_create(aTHX_ pamh, gv_stashpv("PAM::Handle", GV_ADD));
pam_set_data(pamh, MY_INTERPRETER_KEY, my_perl, &cleanup_my_perl);
if (original_interpreter != NULL) {
pam_syslog(pamh, LOG_DEBUG, "We have an original interpreter, set up some state to store it");
pam_set_data(pamh, ORIGINAL_INTERPRETER_KEY, original_interpreter, NULL);
}
pam_syslog(pamh, LOG_DEBUG, "Get ready to invoke the module");
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP, 3 + argc);
XPUSHs(sv_2mortal(module_name));
XPUSHs(sv_2mortal(pamh_sv));
XPUSHs(sv_2mortal(newSViv(flags)));
for (i = 0; i < argc; i++)
XPUSHs(sv_2mortal(newSVpv(argv[i], 0)));
PUTBACK;
call_method(phase, G_SCALAR);
SPAGAIN;
rv = POPi;
PUTBACK;
FREETMPS;
LEAVE;
if (original_interpreter != NULL) {
pam_syslog(pamh, LOG_DEBUG, "Return to the original interpreter context");
PERL_SET_CONTEXT(original_interpreter);
pam_set_data(pamh, ORIGINAL_INTERPRETER_KEY, NULL, NULL);
}
/* Can't use this cause we might not be the last perl interpreter. Really only perl(1) can call this.
else {
PERL_SYS_TERM();
}
*/
return rv;
}
void
cleanup_my_perl(pam_handle_t *pamh, void *data, int error_status)
{
pam_syslog(pamh, LOG_DEBUG, "Cleaning up perl interpreter");
PerlInterpreter *my_perl = (PerlInterpreter*)data;
perl_destruct(my_perl);
perl_free(my_perl);
my_perl = NULL;
}
void
start_perl_callback(pam_handle_t *pamh)
{
PerlInterpreter *original_interpreter;
if (pam_get_data(pamh, ORIGINAL_INTERPRETER_KEY, (void*)&original_interpreter) != PAM_SUCCESS)
original_interpreter = NULL;
if (original_interpreter != NULL) {
PERL_SET_CONTEXT(original_interpreter);
}
}
void
end_perl_callback(pam_handle_t *pamh)
{
PerlInterpreter *my_perl;
if (pam_get_data(pamh, MY_INTERPRETER_KEY, (void*)&my_perl) != PAM_SUCCESS)
my_perl = NULL;
assert(my_perl);
PERL_SET_CONTEXT(my_perl);
}