1 | /** |
---|
2 | * NDG python embedded bbftp daemon module. |
---|
3 | * |
---|
4 | * @author Stephen Pascoe |
---|
5 | * |
---|
6 | * Copyright (C) 2006 CCLRC & NERC |
---|
7 | * |
---|
8 | * This software may be distributed under the terms of the Q Public Licence, version 1.0 or later. |
---|
9 | * |
---|
10 | */ |
---|
11 | |
---|
12 | #include <Python.h> |
---|
13 | #include <ndg.h> |
---|
14 | |
---|
15 | extern char **environ; |
---|
16 | /* |
---|
17 | * variables and prototypes from bbftpd source |
---|
18 | */ |
---|
19 | |
---|
20 | extern int be_daemon; |
---|
21 | extern int newcontrolport; |
---|
22 | |
---|
23 | int bbftpd_main(int argc, char **argv, char **envp); |
---|
24 | int ndg_message_send(char *buffer, int buffertosendlength, char *logmessage) ; |
---|
25 | int ndg_message_recv(char **buffer, int *length, char *logmessage) ; |
---|
26 | |
---|
27 | /* |
---|
28 | * Static variables. |
---|
29 | */ |
---|
30 | static PyObject *authHandler = NULL; |
---|
31 | static PyObject *authzHandler = NULL; |
---|
32 | |
---|
33 | static void ndg_pyerr_to_logmessage(char *logmessage, char *prefix); |
---|
34 | |
---|
35 | /*--------------------------------------------------------------------------------------------------- |
---|
36 | * The following functions interact with bbftpd_private_user. They are placed here to separate |
---|
37 | * python and bbftp APIs. |
---|
38 | * |
---|
39 | */ |
---|
40 | |
---|
41 | /** |
---|
42 | * Copy the python exception message to a bbftp logmessage. |
---|
43 | * |
---|
44 | * If the python error indicator is set it will be cleared. |
---|
45 | * If the python error indicator is not set the empty string will be copied to \a logmessage. |
---|
46 | * The exception message is truncated if it exceeds \c NDG_MAX_LOGMESSAGE. |
---|
47 | * |
---|
48 | * @param logmessage a pointer to a buffer of length \c NDG_MAX_LOGMESSAGE |
---|
49 | * for storing an error message when returning -1. |
---|
50 | * @param prefix a string to be prefixed to the logmessage. |
---|
51 | */ |
---|
52 | static void ndg_pyerr_to_logmessage(char *logmessage, char *prefix) { |
---|
53 | PyObject *ptype, *pvalue, *ptraceback, *obj; |
---|
54 | char *exname, *exvalue; |
---|
55 | |
---|
56 | PyErr_Fetch(&ptype, &pvalue, &ptraceback); |
---|
57 | |
---|
58 | if ((obj = PyObject_GetAttrString(ptype, "__name__")) == NULL) goto error; |
---|
59 | exname = PyString_AsString(obj); Py_DECREF(obj); |
---|
60 | if (exname == NULL) goto error; |
---|
61 | |
---|
62 | if ((obj = PyObject_Str(pvalue)) == NULL) goto error; |
---|
63 | exvalue = PyString_AsString(obj); Py_DECREF(obj); |
---|
64 | if (exvalue == NULL) goto error; |
---|
65 | |
---|
66 | snprintf(logmessage, NDG_MAX_LOGMESSAGE, "%s: %s: %s", prefix, exname, exvalue); |
---|
67 | return; |
---|
68 | |
---|
69 | error: |
---|
70 | sprintf("ndg_pyerr_to_logmessage: internal python error", logmessage); |
---|
71 | return; |
---|
72 | } |
---|
73 | |
---|
74 | /** |
---|
75 | * Make a callback bbftpd->python to handle authentication. |
---|
76 | * |
---|
77 | * @param logmessage a pointer to a buffer of length \c NDG_MAX_LOGMESSAGE |
---|
78 | * for storing an error message when returning -1. |
---|
79 | * @return 0 on success, -1 on failure |
---|
80 | */ |
---|
81 | int ndg_auth(char *logmessage) { |
---|
82 | |
---|
83 | if (authzHandler != NULL) { |
---|
84 | sprintf(logmessage, "ndg_auth: authzHandler already present"); |
---|
85 | return -1; |
---|
86 | } |
---|
87 | |
---|
88 | if (authHandler == NULL) { |
---|
89 | sprintf(logmessage, "ndg_auth: no authHandler set"); |
---|
90 | return -1; |
---|
91 | } |
---|
92 | |
---|
93 | /** @todo exceptions of type delivery.server.AuthenticationFailure should be logged as |
---|
94 | * failure rather than error. |
---|
95 | */ |
---|
96 | if ((authzHandler = PyObject_CallMethod(authHandler, "authorise", "")) == NULL) { |
---|
97 | ndg_pyerr_to_logmessage(logmessage, "ndg_auth"); |
---|
98 | return -1; |
---|
99 | } |
---|
100 | |
---|
101 | /* Authorisation is considered failed if the return value isn't True. */ |
---|
102 | if (!PyObject_IsTrue(authzHandler)) { |
---|
103 | sprintf(logmessage, "ndg_auth: python authorisation failed"); |
---|
104 | return -1; |
---|
105 | } |
---|
106 | |
---|
107 | return 0; |
---|
108 | } |
---|
109 | |
---|
110 | /** |
---|
111 | * retrieve the username from authzHandler. |
---|
112 | * |
---|
113 | * @param logmessage a pointer to a buffer of length \c NDG_MAX_LOGMESSAGE |
---|
114 | * for storing an error message when returning NULL. |
---|
115 | * @return pointer to the username or NULL on failure |
---|
116 | */ |
---|
117 | char *ndg_getusername(char *logmessage) { |
---|
118 | char *username; |
---|
119 | PyObject *str_obj; |
---|
120 | |
---|
121 | if (authzHandler == NULL) { |
---|
122 | sprintf(logmessage, "bbftpd_private_auth_getusername: no authzHandler set"); |
---|
123 | return NULL; |
---|
124 | } |
---|
125 | |
---|
126 | if ((str_obj = PyObject_GetAttrString(authzHandler, "username")) == NULL) { |
---|
127 | ndg_pyerr_to_logmessage(logmessage, "ndg_getusername"); |
---|
128 | return NULL; |
---|
129 | } |
---|
130 | |
---|
131 | if ((username = PyString_AsString(str_obj)) == NULL) { |
---|
132 | ndg_pyerr_to_logmessage(logmessage, "ndg_getusername"); |
---|
133 | Py_DECREF(str_obj); |
---|
134 | return NULL; |
---|
135 | } |
---|
136 | |
---|
137 | return username; |
---|
138 | } |
---|
139 | |
---|
140 | |
---|
141 | /** |
---|
142 | * Make a callback to do authorisation of a control command. |
---|
143 | * |
---|
144 | * @see bbftpd_private_authz_control() |
---|
145 | */ |
---|
146 | int ndg_authz_control(int msgcode, int transferoption, char *path, char *logmessage) { |
---|
147 | PyObject *ret_obj; |
---|
148 | |
---|
149 | if (authzHandler == NULL) { |
---|
150 | sprintf(logmessage, "ndg_authz_control: no authzHandler set"); |
---|
151 | return -1; |
---|
152 | } |
---|
153 | |
---|
154 | if ((ret_obj = PyObject_CallMethod(authzHandler, "authzControl", "iis", msgcode, transferoption, path)) == NULL) { |
---|
155 | ndg_pyerr_to_logmessage(logmessage, "ndg_authz_control"); |
---|
156 | return -1; |
---|
157 | } |
---|
158 | |
---|
159 | /* Authorisation fails if ret_obj is false. */ |
---|
160 | if (!PyObject_IsTrue(ret_obj)) { |
---|
161 | ndg_pyerr_to_logmessage(logmessage, "ndg_authz_control"); |
---|
162 | Py_DECREF(ret_obj); |
---|
163 | return -1; |
---|
164 | } |
---|
165 | |
---|
166 | return 0; |
---|
167 | } |
---|
168 | |
---|
169 | /** |
---|
170 | * Make a callback to do authorisation of a retr command. |
---|
171 | * |
---|
172 | * @see bbftpd_private_authz_retr() |
---|
173 | */ |
---|
174 | int ndg_authz_retr(char *path, char *logmessage) { |
---|
175 | PyObject *ret_obj; |
---|
176 | |
---|
177 | if (authzHandler == NULL) { |
---|
178 | sprintf(logmessage, "ndg_authz_retr: no authzHandler set"); |
---|
179 | return -1; |
---|
180 | } |
---|
181 | |
---|
182 | if ((ret_obj = PyObject_CallMethod(authzHandler, "authzRetr", "s", path)) == NULL) { |
---|
183 | ndg_pyerr_to_logmessage(logmessage, "ndg_authz_retr"); |
---|
184 | return -1; |
---|
185 | } |
---|
186 | |
---|
187 | /* Authorisation fails if ret_obj is false. */ |
---|
188 | if (!PyObject_IsTrue(ret_obj)) { |
---|
189 | ndg_pyerr_to_logmessage(logmessage, "ndg_authz_retr"); |
---|
190 | Py_DECREF(ret_obj); |
---|
191 | return -1; |
---|
192 | } |
---|
193 | |
---|
194 | return 0; |
---|
195 | } |
---|
196 | |
---|
197 | |
---|
198 | /** |
---|
199 | * Make a callback to do authorisation of a store command. |
---|
200 | * |
---|
201 | * @see bbftpd_private_authz_store() |
---|
202 | */ |
---|
203 | int ndg_authz_store(char *path, char *logmessage) { |
---|
204 | PyObject *ret_obj; |
---|
205 | |
---|
206 | if (authzHandler == NULL) { |
---|
207 | sprintf(logmessage, "ndg_authz_store: no authzHandler set"); |
---|
208 | return -1; |
---|
209 | } |
---|
210 | |
---|
211 | if ((ret_obj = PyObject_CallMethod(authzHandler, "authzStore", "s", path)) == NULL) { |
---|
212 | ndg_pyerr_to_logmessage(logmessage, "ndg_authz_store"); |
---|
213 | return -1; |
---|
214 | } |
---|
215 | |
---|
216 | /* Authorisation fails if ret_obj is false. */ |
---|
217 | if (!PyObject_IsTrue(ret_obj)) { |
---|
218 | ndg_pyerr_to_logmessage(logmessage, "ndg_authz_store"); |
---|
219 | Py_DECREF(ret_obj); |
---|
220 | return -1; |
---|
221 | } |
---|
222 | |
---|
223 | return 0; |
---|
224 | } |
---|
225 | |
---|
226 | |
---|
227 | |
---|
228 | /*------------------------------------------------------------------------------------------------- |
---|
229 | * Functions exported to python |
---|
230 | * |
---|
231 | */ |
---|
232 | |
---|
233 | /* send(string) */ |
---|
234 | static PyObject *bbftpd_send(PyObject *self, PyObject *args) { |
---|
235 | char *buffer, logmessage[1024]; |
---|
236 | int len; |
---|
237 | |
---|
238 | if (!PyArg_ParseTuple(args, "s#", &buffer, &len)) { |
---|
239 | return NULL; |
---|
240 | } |
---|
241 | |
---|
242 | if (authHandler == NULL) { |
---|
243 | PyErr_SetString(PyExc_RuntimeError, "Must be called within a bbftpd callback"); |
---|
244 | return NULL; |
---|
245 | } |
---|
246 | |
---|
247 | if (ndg_message_send(buffer, len, logmessage) == -1) { |
---|
248 | PyErr_SetString(PyExc_IOError, logmessage); |
---|
249 | return NULL; |
---|
250 | } |
---|
251 | |
---|
252 | Py_RETURN_NONE; |
---|
253 | } |
---|
254 | |
---|
255 | /* recv(length) -> string */ |
---|
256 | static PyObject *bbftpd_recv(PyObject *self, PyObject *args) { |
---|
257 | char *buffer, logmessage[1024]; |
---|
258 | int len; |
---|
259 | PyObject *ret; |
---|
260 | |
---|
261 | if (!PyArg_ParseTuple(args, "")) { |
---|
262 | return NULL; |
---|
263 | } |
---|
264 | |
---|
265 | if (authHandler == NULL) { |
---|
266 | PyErr_SetString(PyExc_RuntimeError, "Must be called within a bbftpd callback"); |
---|
267 | return NULL; |
---|
268 | } |
---|
269 | |
---|
270 | if (ndg_message_recv(&buffer, &len, logmessage) == -1) { |
---|
271 | PyErr_SetString(PyExc_IOError, logmessage); |
---|
272 | free(buffer); |
---|
273 | return NULL; |
---|
274 | } |
---|
275 | |
---|
276 | ret = Py_BuildValue("s#", buffer, len); |
---|
277 | free(buffer); |
---|
278 | return ret; |
---|
279 | } |
---|
280 | |
---|
281 | |
---|
282 | |
---|
283 | /** |
---|
284 | * Main entry point for the python module. |
---|
285 | */ |
---|
286 | static PyObject *bbftpd_run(PyObject *self, PyObject *args) { |
---|
287 | int argc, i; |
---|
288 | char **argv, **arg_p; |
---|
289 | int pid; |
---|
290 | char preargs[2][20] = { "bbftpd_embedded", "-b" }; |
---|
291 | PyObject *daemon_args, *item; |
---|
292 | |
---|
293 | if (!PyArg_ParseTuple(args, "OO", &authHandler, &daemon_args)) { |
---|
294 | return NULL; |
---|
295 | } |
---|
296 | Py_INCREF(authHandler); |
---|
297 | Py_INCREF(daemon_args); |
---|
298 | |
---|
299 | /* |
---|
300 | * Convert arguments into a standard argv sequence. |
---|
301 | */ |
---|
302 | argc = PySequence_Size(daemon_args); |
---|
303 | if ((argv = (char**)malloc((argc + 2)*sizeof(char*))) == NULL) { |
---|
304 | PyErr_SetString(PyExc_MemoryError, "malloc failed"); |
---|
305 | Py_DECREF(daemon_args); |
---|
306 | return NULL; |
---|
307 | } |
---|
308 | |
---|
309 | // Add fixed arguments to daemon's argv |
---|
310 | arg_p = argv; |
---|
311 | *arg_p = preargs[0]; arg_p++; |
---|
312 | *arg_p = preargs[1]; arg_p++; |
---|
313 | |
---|
314 | for (i=0; i<argc; i++) { |
---|
315 | if ((item = PySequence_GetItem(daemon_args, i)) == NULL) { |
---|
316 | Py_DECREF(authHandler); |
---|
317 | Py_DECREF(daemon_args); |
---|
318 | return NULL; |
---|
319 | } |
---|
320 | if ((*arg_p = PyString_AsString(item)) == NULL) { |
---|
321 | free(argv); |
---|
322 | Py_DECREF(item); |
---|
323 | Py_DECREF(authHandler); |
---|
324 | Py_DECREF(daemon_args); |
---|
325 | return NULL; |
---|
326 | } |
---|
327 | arg_p++; |
---|
328 | Py_DECREF(item); |
---|
329 | } |
---|
330 | |
---|
331 | /* |
---|
332 | * We must reset some global variables so that bbftpd_run can be executed multiple times. |
---|
333 | */ |
---|
334 | be_daemon = 0; |
---|
335 | |
---|
336 | pid = bbftpd_main(argc+2, argv, environ); |
---|
337 | |
---|
338 | free(argv); |
---|
339 | Py_DECREF(authHandler); authHandler = NULL; |
---|
340 | Py_DECREF(daemon_args); |
---|
341 | |
---|
342 | return Py_BuildValue("i", pid); |
---|
343 | } |
---|
344 | |
---|
345 | static PyMethodDef BbftpdMethods[] = { |
---|
346 | { |
---|
347 | "run", bbftpd_run, METH_VARARGS, |
---|
348 | "Execute the bbftpd server.\n" |
---|
349 | "\n" |
---|
350 | "Forks an embedded bbftpd process as if it were executed\n" |
---|
351 | "with the command line $ bbftpd -b <args>." |
---|
352 | "\n" |
---|
353 | "@param args: command line arguments to pass to the server\n" |
---|
354 | "@return: the PID of the server process\n" |
---|
355 | }, |
---|
356 | { |
---|
357 | "send", bbftpd_send, METH_VARARGS, |
---|
358 | "Send an authentication message to the client.\n" |
---|
359 | "\n" |
---|
360 | "Equivilent to bbftpd_private_send() C call.\n" |
---|
361 | "\n" |
---|
362 | "@param buffer: A string containing the message\n" |
---|
363 | "@raise IOError: if bbftpd_private_send() call fails\n" |
---|
364 | }, |
---|
365 | { |
---|
366 | "recv", bbftpd_recv, METH_VARARGS, |
---|
367 | "Receive an authentication message from the client.\n" |
---|
368 | "\n" |
---|
369 | "Equivilent to bbftpd_private_recv() C call.\n" |
---|
370 | "\n" |
---|
371 | "@param len: The length of message to expect\n" |
---|
372 | "@return: A string containing the message\n" |
---|
373 | "@raise IOError: if bbftpd_private_recv() call fails\n" |
---|
374 | }, |
---|
375 | {NULL, NULL, 0, NULL} |
---|
376 | }; |
---|
377 | |
---|
378 | PyMODINIT_FUNC initbbftpd(void) { |
---|
379 | (void) Py_InitModule("bbftpd", BbftpdMethods); |
---|
380 | } |
---|
381 | |
---|