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