General cleanup of compiler warnings etc.
[tprouty/samba.git] / source / python / py_winbind.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Python wrapper for winbind client functions.
5
6    Copyright (C) Tim Potter      2002
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "Python.h"
25
26 #include "py_common.h"
27
28 /* 
29  * Exceptions raised by this module 
30  */
31
32 PyObject *winbind_error;        /* A winbind call returned WINBINDD_ERROR */
33
34 /* Prototypes from common.h */
35
36 NSS_STATUS winbindd_request(int req_type, 
37                             struct winbindd_request *request,
38                             struct winbindd_response *response);
39
40 /*
41  * Name <-> SID conversion
42  */
43
44 /* Convert a name to a sid */
45
46 static PyObject *py_name_to_sid(PyObject *self, PyObject *args)
47
48 {
49         struct winbindd_request request;
50         struct winbindd_response response;
51         PyObject *result;
52         char *name, *p, *sep;
53
54         if (!PyArg_ParseTuple(args, "s", &name))
55                 return NULL;
56
57         ZERO_STRUCT(request);
58         ZERO_STRUCT(response);
59
60         sep = lp_winbind_separator();
61
62         if ((p = strchr(name, sep[0]))) {
63                 *p = 0;
64                 fstrcpy(request.data.name.dom_name, name);
65                 fstrcpy(request.data.name.name, p + 1);
66         } else {
67                 fstrcpy(request.data.name.dom_name, lp_workgroup());
68                 fstrcpy(request.data.name.name, name);
69         }
70
71         if (winbindd_request(WINBINDD_LOOKUPNAME, &request, &response)  
72             != NSS_STATUS_SUCCESS) {
73                 PyErr_SetString(winbind_error, "lookup failed");
74                 return NULL;
75         }
76
77         result = PyString_FromString(response.data.sid.sid);
78
79         return result;
80 }
81
82 /* Convert a sid to a name */
83
84 static PyObject *py_sid_to_name(PyObject *self, PyObject *args)
85 {
86         struct winbindd_request request;
87         struct winbindd_response response;
88         PyObject *result;
89         char *sid, *name;
90
91         if (!PyArg_ParseTuple(args, "s", &sid))
92                 return NULL;
93
94         ZERO_STRUCT(request);
95         ZERO_STRUCT(response);
96
97         fstrcpy(request.data.sid, sid);
98
99         if (winbindd_request(WINBINDD_LOOKUPSID, &request, &response)  
100             != NSS_STATUS_SUCCESS) {
101                 PyErr_SetString(winbind_error, "lookup failed");
102                 return NULL;
103         }
104
105         asprintf(&name, "%s%s%s", response.data.name.dom_name,
106                  lp_winbind_separator(), response.data.name.name);
107
108         result = PyString_FromString(name);
109
110         free(name);
111
112         return result;
113 }
114
115 /*
116  * Enumerate users/groups
117  */
118
119 /* Enumerate domain users */
120
121 static PyObject *py_enum_domain_users(PyObject *self, PyObject *args)
122 {
123         struct winbindd_response response;
124         PyObject *result;
125
126         if (!PyArg_ParseTuple(args, ""))
127                 return NULL;
128
129         ZERO_STRUCT(response);
130
131         if (winbindd_request(WINBINDD_LIST_USERS, NULL, &response) 
132             != NSS_STATUS_SUCCESS) {
133                 PyErr_SetString(winbind_error, "lookup failed");
134                 return NULL;            
135         }
136
137         result = PyList_New(0);
138
139         if (response.extra_data) {
140                 char *extra_data = response.extra_data;
141                 fstring name;
142
143                 while (next_token(&extra_data, name, ",", sizeof(fstring)))
144                         PyList_Append(result, PyString_FromString(name));
145         }
146
147         return result;
148 }
149
150 /* Enumerate domain groups */
151
152 static PyObject *py_enum_domain_groups(PyObject *self, PyObject *args)
153 {
154         struct winbindd_response response;
155         PyObject *result = NULL;
156
157         if (!PyArg_ParseTuple(args, ""))
158                 return NULL;
159
160         ZERO_STRUCT(response);
161
162         if (winbindd_request(WINBINDD_LIST_GROUPS, NULL, &response) 
163             != NSS_STATUS_SUCCESS) {
164                 PyErr_SetString(winbind_error, "lookup failed");
165                 return NULL;            
166         }
167
168         result = PyList_New(0);
169
170         if (response.extra_data) {
171                 char *extra_data = response.extra_data;
172                 fstring name;
173
174                 while (next_token(&extra_data, name, ",", sizeof(fstring)))
175                         PyList_Append(result, PyString_FromString(name));
176         }
177
178         return result;
179 }
180
181 /*
182  * Miscellaneous domain related
183  */
184
185 /* Enumerate domain groups */
186
187 static PyObject *py_enum_trust_dom(PyObject *self, PyObject *args)
188 {
189         struct winbindd_response response;
190         PyObject *result = NULL;
191
192         if (!PyArg_ParseTuple(args, ""))
193                 return NULL;
194
195         ZERO_STRUCT(response);
196
197         if (winbindd_request(WINBINDD_LIST_TRUSTDOM, NULL, &response) 
198             != NSS_STATUS_SUCCESS) {
199                 PyErr_SetString(winbind_error, "lookup failed");
200                 return NULL;            
201         }
202
203         result = PyList_New(0);
204
205         if (response.extra_data) {
206                 char *extra_data = response.extra_data;
207                 fstring name;
208
209                 while (next_token(&extra_data, name, ",", sizeof(fstring)))
210                         PyList_Append(result, PyString_FromString(name));
211         }
212
213         return result;
214 }
215
216 /* Check machine account password */
217
218 static PyObject *py_check_secret(PyObject *self, PyObject *args)
219 {
220         struct winbindd_response response;
221
222         if (!PyArg_ParseTuple(args, ""))
223                 return NULL;
224
225         ZERO_STRUCT(response);
226
227         if (winbindd_request(WINBINDD_CHECK_MACHACC, NULL, &response) 
228             != NSS_STATUS_SUCCESS) {
229                 PyErr_SetString(winbind_error, "lookup failed");
230                 return NULL;            
231         }
232
233         return PyInt_FromLong(response.data.num_entries);
234 }
235
236 /*
237  * Return a dictionary consisting of all the winbind related smb.conf
238  * parameters.  This is stored in the module object.
239  */
240
241 static PyObject *py_config_dict(void)
242 {
243         PyObject *result;
244         uid_t ulow, uhi;
245         gid_t glow, ghi;
246         
247         if (!(result = PyDict_New()))
248                 return NULL;
249
250         /* Various string parameters */
251
252         PyDict_SetItemString(result, "workgroup", 
253                              PyString_FromString(lp_workgroup()));
254
255         PyDict_SetItemString(result, "separator", 
256                              PyString_FromString(lp_winbind_separator()));
257
258         PyDict_SetItemString(result, "template_homedir", 
259                              PyString_FromString(lp_template_homedir()));
260
261         PyDict_SetItemString(result, "template_shell", 
262                              PyString_FromString(lp_template_shell()));
263
264         /* Winbind uid/gid range */
265
266         if (lp_winbind_uid(&ulow, &uhi)) {
267                 PyDict_SetItemString(result, "uid_low", PyInt_FromLong(ulow));
268                 PyDict_SetItemString(result, "uid_high", PyInt_FromLong(uhi));
269         }
270
271         if (lp_winbind_gid(&glow, &ghi)) {
272                 PyDict_SetItemString(result, "gid_low", PyInt_FromLong(glow));
273                 PyDict_SetItemString(result, "gid_high", PyInt_FromLong(ghi));
274         }
275
276         return result;
277 }
278
279 /*
280  * ID mapping
281  */
282
283 /* Convert a uid to a SID */
284
285 static PyObject *py_uid_to_sid(PyObject *self, PyObject *args)
286 {
287         struct winbindd_request request;
288         struct winbindd_response response;
289         int id;
290
291         if (!PyArg_ParseTuple(args, "i", &id))
292                 return NULL;
293
294         ZERO_STRUCT(request);
295         ZERO_STRUCT(response);
296
297         request.data.uid = id;
298
299         if (winbindd_request(WINBINDD_UID_TO_SID, &request, &response) 
300             != NSS_STATUS_SUCCESS) {
301                 PyErr_SetString(winbind_error, "lookup failed");
302                 return NULL;            
303         }
304
305         return PyString_FromString(response.data.sid.sid);
306 }
307
308 /* Convert a gid to a SID */
309
310 static PyObject *py_gid_to_sid(PyObject *self, PyObject *args)
311 {
312         struct winbindd_request request;
313         struct winbindd_response response;
314         int id;
315
316         if (!PyArg_ParseTuple(args, "i", &id))
317                 return NULL;
318
319         ZERO_STRUCT(request);
320         ZERO_STRUCT(response);
321
322         request.data.gid = id;
323
324         if (winbindd_request(WINBINDD_GID_TO_SID, &request, &response) 
325             != NSS_STATUS_SUCCESS) {
326                 PyErr_SetString(winbind_error, "lookup failed");
327                 return NULL;            
328         }
329
330         return PyString_FromString(response.data.sid.sid);
331 }
332
333 /* Convert a sid to a uid */
334
335 static PyObject *py_sid_to_uid(PyObject *self, PyObject *args)
336 {
337         struct winbindd_request request;
338         struct winbindd_response response;
339         char *sid;
340
341         if (!PyArg_ParseTuple(args, "s", &sid))
342                 return NULL;
343
344         ZERO_STRUCT(request);
345         ZERO_STRUCT(response);
346
347         fstrcpy(request.data.sid, sid);
348
349         if (winbindd_request(WINBINDD_SID_TO_UID, &request, &response) 
350             != NSS_STATUS_SUCCESS) {
351                 PyErr_SetString(winbind_error, "lookup failed");
352                 return NULL;            
353         }
354
355         return PyInt_FromLong(response.data.uid);
356 }
357
358 /* Convert a sid to a gid */
359
360 static PyObject *py_sid_to_gid(PyObject *self, PyObject *args)
361 {
362         struct winbindd_request request;
363         struct winbindd_response response;
364         char *sid;
365
366         if (!PyArg_ParseTuple(args, "s", &sid))
367                 return NULL;
368
369         ZERO_STRUCT(request);
370         ZERO_STRUCT(response);
371
372         fstrcpy(request.data.sid, sid);
373
374         if (winbindd_request(WINBINDD_SID_TO_GID, &request, &response) 
375             != NSS_STATUS_SUCCESS) {
376                 PyErr_SetString(winbind_error, "lookup failed");
377                 return NULL;            
378         }
379         
380         return PyInt_FromLong(response.data.gid);
381 }
382
383 /*
384  * PAM authentication functions
385  */
386
387 /* Plaintext authentication */
388
389 static PyObject *py_auth_plaintext(PyObject *self, PyObject *args)
390 {
391         struct winbindd_request request;
392         struct winbindd_response response;
393         char *username, *password;
394
395         if (!PyArg_ParseTuple(args, "ss", &username, &password))
396                 return NULL;
397
398         ZERO_STRUCT(request);
399         ZERO_STRUCT(response);
400
401         fstrcpy(request.data.auth.user, username);
402         fstrcpy(request.data.auth.pass, password);
403
404         if (winbindd_request(WINBINDD_PAM_AUTH, &request, &response) 
405             != NSS_STATUS_SUCCESS) {
406                 PyErr_SetString(winbind_error, "lookup failed");
407                 return NULL;            
408         }
409         
410         return PyInt_FromLong(response.data.auth.nt_status);
411 }
412
413 /* Challenge/response authentication */
414
415 static PyObject *py_auth_crap(PyObject *self, PyObject *args)
416 {
417         struct winbindd_request request;
418         struct winbindd_response response;
419         char *username, *password;
420
421         if (!PyArg_ParseTuple(args, "ss", &username, &password))
422                 return NULL;
423
424         ZERO_STRUCT(request);
425         ZERO_STRUCT(response);
426
427         fstrcpy(request.data.auth_crap.user, username);
428
429         generate_random_buffer(request.data.auth_crap.chal, 8, False);
430         
431         SMBencrypt((uchar *)password, request.data.auth_crap.chal, 
432                    (uchar *)request.data.auth_crap.lm_resp);
433         SMBNTencrypt((uchar *)password, request.data.auth_crap.chal,
434                      (uchar *)request.data.auth_crap.nt_resp);
435
436         request.data.auth_crap.lm_resp_len = 24;
437         request.data.auth_crap.nt_resp_len = 24;
438
439         if (winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response) 
440             != NSS_STATUS_SUCCESS) {
441                 PyErr_SetString(winbind_error, "lookup failed");
442                 return NULL;            
443         }
444         
445         return PyInt_FromLong(response.data.auth.nt_status);
446 }
447
448 /*
449  * Method dispatch table
450  */
451
452 static PyMethodDef winbind_methods[] = {
453
454         /* Name <-> SID conversion */
455
456         { "name_to_sid", py_name_to_sid, METH_VARARGS,
457           "name_to_sid(s) -> string
458
459 Return the SID for a name.
460
461 Example:
462
463 >>> winbind.name_to_sid('FOO/Administrator')
464 'S-1-5-21-406022937-1377575209-526660263-500' " },
465
466         { "sid_to_name", py_sid_to_name, METH_VARARGS,
467           "sid_to_name(s) -> string
468
469 Return the name for a SID.
470
471 Example:
472
473 >>> import winbind
474 >>> winbind.sid_to_name('S-1-5-21-406022937-1377575209-526660263-500')
475 'FOO/Administrator' " },
476
477         /* Enumerate users/groups */
478
479         { "enum_domain_users", py_enum_domain_users, METH_VARARGS,
480           "enum_domain_users() -> list of strings
481
482 Return a list of domain users.
483
484 Example:
485
486 >>> winbind.enum_domain_users()
487 ['FOO/Administrator', 'FOO/anna', 'FOO/Anne Elk', 'FOO/build', 
488 'FOO/foo', 'FOO/foo2', 'FOO/foo3', 'FOO/Guest', 'FOO/user1', 
489 'FOO/whoops-ptang'] " },
490
491         { "enum_domain_groups", py_enum_domain_groups, METH_VARARGS,
492           "enum_domain_groups() -> list of strings
493
494 Return a list of domain groups.
495
496 Example:
497
498 >>> winbind.enum_domain_groups()
499 ['FOO/cows', 'FOO/Domain Admins', 'FOO/Domain Guests', 
500 'FOO/Domain Users'] " },
501
502         /* ID mapping */
503
504         { "uid_to_sid", py_uid_to_sid, METH_VARARGS,
505           "uid_to_sid(int) -> string
506
507 Return the SID for a UNIX uid.
508
509 Example:
510
511 >>> winbind.uid_to_sid(10000)   
512 'S-1-5-21-406022937-1377575209-526660263-500' " },
513
514         { "gid_to_sid", py_gid_to_sid, METH_VARARGS,
515           "gid_to_sid(int) -> string
516
517 Return the UNIX gid for a SID.
518
519 Example:
520
521 >>> winbind.gid_to_sid(10001)
522 'S-1-5-21-406022937-1377575209-526660263-512' " },
523
524         { "sid_to_uid", py_sid_to_uid, METH_VARARGS,
525           "sid_to_uid(string) -> int
526
527 Return the UNIX uid for a SID.
528
529 Example:
530
531 >>> winbind.sid_to_uid('S-1-5-21-406022937-1377575209-526660263-500')
532 10000 " },
533
534         { "sid_to_gid", py_sid_to_gid, METH_VARARGS,
535           "sid_to_gid(string) -> int
536
537 Return the UNIX gid corresponding to a SID.
538
539 Example:
540
541 >>> winbind.sid_to_gid('S-1-5-21-406022937-1377575209-526660263-512')
542 10001 " },
543
544         /* Miscellaneous */
545
546         { "check_secret", py_check_secret, METH_VARARGS,
547           "check_secret() -> int
548
549 Check the machine trust account password.  The NT status is returned
550 with zero indicating success. " },
551
552         { "enum_trust_dom", py_enum_trust_dom, METH_VARARGS,
553           "enum_trust_dom() -> list of strings
554
555 Return a list of trusted domains.  The domain the server is a member 
556 of is not included.
557
558 Example:
559
560 >>> winbind.enum_trust_dom()
561 ['NPSD-TEST2', 'SP2NDOM'] " },
562
563         /* PAM authorisation functions */
564
565         { "auth_plaintext", py_auth_plaintext, METH_VARARGS,
566           "auth_plaintext(s, s) -> int
567
568 Authenticate a username and password using plaintext authentication.
569 The NT status code is returned with zero indicating success." },
570
571         { "auth_crap", py_auth_crap, METH_VARARGS,
572           "auth_crap(s, s) -> int
573
574 Authenticate a username and password using the challenge/response
575 protocol.  The NT status code is returned with zero indicating
576 success." },
577
578         { NULL }
579 };
580
581 static struct const_vals {
582         char *name;
583         uint32 value;
584         char *docstring;
585 } module_const_vals[] = {
586
587         /* Well known RIDs */
588         
589         { "DOMAIN_USER_RID_ADMIN", DOMAIN_USER_RID_ADMIN, 
590           "Well-known RID for Administrator user" },
591
592         { "DOMAIN_USER_RID_GUEST", DOMAIN_USER_RID_GUEST,
593           "Well-known RID for Guest user" },
594
595         { "DOMAIN_GROUP_RID_ADMINS", DOMAIN_GROUP_RID_ADMINS,
596           "Well-known RID for Domain Admins group" },
597
598         { "DOMAIN_GROUP_RID_USERS", DOMAIN_GROUP_RID_USERS,
599           "Well-known RID for Domain Users group" },
600
601         { "DOMAIN_GROUP_RID_GUESTS", DOMAIN_GROUP_RID_GUESTS,
602           "Well-known RID for Domain Guests group" }, 
603         
604         { NULL }
605 };
606
607 static void const_init(PyObject *dict)
608 {
609         struct const_vals *tmp;
610         PyObject *obj;
611
612         for (tmp = module_const_vals; tmp->name; tmp++) {
613                 obj = PyInt_FromLong(tmp->value);
614                 PyDict_SetItemString(dict, tmp->name, obj);
615                 Py_DECREF(obj);
616         }
617 }
618
619 /*
620  * Module initialisation 
621  */
622
623 static char winbind_module__doc__[] =
624 "A python extension to winbind client functions.";
625
626 void initwinbind(void)
627 {
628         PyObject *module, *dict;
629
630         /* Initialise module */
631
632         module = Py_InitModule3("winbind", winbind_methods,
633                                 winbind_module__doc__);
634
635         dict = PyModule_GetDict(module);
636
637         winbind_error = PyErr_NewException("winbind.error", NULL, NULL);
638         PyDict_SetItemString(dict, "error", winbind_error);
639
640         /* Do samba initialisation */
641
642         py_samba_init();
643
644         /* Initialise constants */
645
646         const_init(dict);
647
648         /* Insert configuration dictionary */
649
650         PyDict_SetItemString(dict, "config", py_config_dict());
651 }