This commit was manufactured by cvs2svn to create branch 'SAMBA_3_0'.
[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-2003
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 "py_winbind.h"
24
25 /* 
26  * Exceptions raised by this module 
27  */
28
29 PyObject *winbind_error;        /* A winbind call returned WINBINDD_ERROR */
30
31 /* Prototypes from common.h */
32
33 NSS_STATUS winbindd_request(int req_type, 
34                             struct winbindd_request *request,
35                             struct winbindd_response *response);
36
37 /*
38  * Name <-> SID conversion
39  */
40
41 /* Convert a name to a sid */
42
43 static PyObject *py_name_to_sid(PyObject *self, PyObject *args)
44
45 {
46         struct winbindd_request request;
47         struct winbindd_response response;
48         PyObject *result;
49         char *name, *p;
50         const char *sep;
51
52         if (!PyArg_ParseTuple(args, "s", &name))
53                 return NULL;
54
55         ZERO_STRUCT(request);
56         ZERO_STRUCT(response);
57
58         sep = lp_winbind_separator();
59
60         if ((p = strchr(name, sep[0]))) {
61                 *p = 0;
62                 fstrcpy(request.data.name.dom_name, name);
63                 fstrcpy(request.data.name.name, p + 1);
64         } else {
65                 fstrcpy(request.data.name.dom_name, lp_workgroup());
66                 fstrcpy(request.data.name.name, name);
67         }
68
69         if (winbindd_request(WINBINDD_LOOKUPNAME, &request, &response)  
70             != NSS_STATUS_SUCCESS) {
71                 PyErr_SetString(winbind_error, "lookup failed");
72                 return NULL;
73         }
74
75         result = PyString_FromString(response.data.sid.sid);
76
77         return result;
78 }
79
80 /* Convert a sid to a name */
81
82 static PyObject *py_sid_to_name(PyObject *self, PyObject *args)
83 {
84         struct winbindd_request request;
85         struct winbindd_response response;
86         PyObject *result;
87         char *sid, *name;
88
89         if (!PyArg_ParseTuple(args, "s", &sid))
90                 return NULL;
91
92         ZERO_STRUCT(request);
93         ZERO_STRUCT(response);
94
95         fstrcpy(request.data.sid, sid);
96
97         if (winbindd_request(WINBINDD_LOOKUPSID, &request, &response)  
98             != NSS_STATUS_SUCCESS) {
99                 PyErr_SetString(winbind_error, "lookup failed");
100                 return NULL;
101         }
102
103         asprintf(&name, "%s%s%s", response.data.name.dom_name,
104                  lp_winbind_separator(), response.data.name.name);
105
106         result = PyString_FromString(name);
107
108         free(name);
109
110         return result;
111 }
112
113 /*
114  * Enumerate users/groups
115  */
116
117 /* Enumerate domain users */
118
119 static PyObject *py_enum_domain_users(PyObject *self, PyObject *args)
120 {
121         struct winbindd_response response;
122         PyObject *result;
123
124         if (!PyArg_ParseTuple(args, ""))
125                 return NULL;
126
127         ZERO_STRUCT(response);
128
129         if (winbindd_request(WINBINDD_LIST_USERS, NULL, &response) 
130             != NSS_STATUS_SUCCESS) {
131                 PyErr_SetString(winbind_error, "lookup failed");
132                 return NULL;            
133         }
134
135         result = PyList_New(0);
136
137         if (response.extra_data) {
138                 const char *extra_data = response.extra_data;
139                 fstring name;
140
141                 while (next_token(&extra_data, name, ",", sizeof(fstring)))
142                         PyList_Append(result, PyString_FromString(name));
143         }
144
145         return result;
146 }
147
148 /* Enumerate domain groups */
149
150 static PyObject *py_enum_domain_groups(PyObject *self, PyObject *args)
151 {
152         struct winbindd_response response;
153         PyObject *result = NULL;
154
155         if (!PyArg_ParseTuple(args, ""))
156                 return NULL;
157
158         ZERO_STRUCT(response);
159
160         if (winbindd_request(WINBINDD_LIST_GROUPS, NULL, &response) 
161             != NSS_STATUS_SUCCESS) {
162                 PyErr_SetString(winbind_error, "lookup failed");
163                 return NULL;            
164         }
165
166         result = PyList_New(0);
167
168         if (response.extra_data) {
169                 const char *extra_data = response.extra_data;
170                 fstring name;
171
172                 while (next_token(&extra_data, name, ",", sizeof(fstring)))
173                         PyList_Append(result, PyString_FromString(name));
174         }
175
176         return result;
177 }
178
179 /*
180  * Miscellaneous domain related
181  */
182
183 /* Enumerate domain groups */
184
185 static PyObject *py_enum_trust_dom(PyObject *self, PyObject *args)
186 {
187         struct winbindd_response response;
188         PyObject *result = NULL;
189
190         if (!PyArg_ParseTuple(args, ""))
191                 return NULL;
192
193         ZERO_STRUCT(response);
194
195         if (winbindd_request(WINBINDD_LIST_TRUSTDOM, NULL, &response) 
196             != NSS_STATUS_SUCCESS) {
197                 PyErr_SetString(winbind_error, "lookup failed");
198                 return NULL;            
199         }
200
201         result = PyList_New(0);
202
203         if (response.extra_data) {
204                 const char *extra_data = response.extra_data;
205                 fstring name;
206
207                 while (next_token(&extra_data, name, ",", sizeof(fstring)))
208                         PyList_Append(result, PyString_FromString(name));
209         }
210
211         return result;
212 }
213
214 /* Check machine account password */
215
216 static PyObject *py_check_secret(PyObject *self, PyObject *args)
217 {
218         struct winbindd_response response;
219
220         if (!PyArg_ParseTuple(args, ""))
221                 return NULL;
222
223         ZERO_STRUCT(response);
224
225         if (winbindd_request(WINBINDD_CHECK_MACHACC, NULL, &response) 
226             != NSS_STATUS_SUCCESS) {
227                 PyErr_SetString(winbind_error, "lookup failed");
228                 return NULL;            
229         }
230
231         return PyInt_FromLong(response.data.num_entries);
232 }
233
234 /*
235  * Return a dictionary consisting of all the winbind related smb.conf
236  * parameters.  This is stored in the module object.
237  */
238
239 static PyObject *py_config_dict(void)
240 {
241         PyObject *result;
242         uid_t ulow, uhi;
243         gid_t glow, ghi;
244         
245         if (!(result = PyDict_New()))
246                 return NULL;
247
248         /* Various string parameters */
249
250         PyDict_SetItemString(result, "workgroup", 
251                              PyString_FromString(lp_workgroup()));
252
253         PyDict_SetItemString(result, "separator", 
254                              PyString_FromString(lp_winbind_separator()));
255
256         PyDict_SetItemString(result, "template_homedir", 
257                              PyString_FromString(lp_template_homedir()));
258
259         PyDict_SetItemString(result, "template_shell", 
260                              PyString_FromString(lp_template_shell()));
261
262         /* Winbind uid/gid range */
263
264         if (lp_winbind_uid(&ulow, &uhi)) {
265                 PyDict_SetItemString(result, "uid_low", PyInt_FromLong(ulow));
266                 PyDict_SetItemString(result, "uid_high", PyInt_FromLong(uhi));
267         }
268
269         if (lp_winbind_gid(&glow, &ghi)) {
270                 PyDict_SetItemString(result, "gid_low", PyInt_FromLong(glow));
271                 PyDict_SetItemString(result, "gid_high", PyInt_FromLong(ghi));
272         }
273
274         return result;
275 }
276
277 /*
278  * ID mapping
279  */
280
281 /* Convert a uid to a SID */
282
283 static PyObject *py_uid_to_sid(PyObject *self, PyObject *args)
284 {
285         struct winbindd_request request;
286         struct winbindd_response response;
287         int id;
288
289         if (!PyArg_ParseTuple(args, "i", &id))
290                 return NULL;
291
292         ZERO_STRUCT(request);
293         ZERO_STRUCT(response);
294
295         request.data.uid = id;
296
297         if (winbindd_request(WINBINDD_UID_TO_SID, &request, &response) 
298             != NSS_STATUS_SUCCESS) {
299                 PyErr_SetString(winbind_error, "lookup failed");
300                 return NULL;            
301         }
302
303         return PyString_FromString(response.data.sid.sid);
304 }
305
306 /* Convert a gid to a SID */
307
308 static PyObject *py_gid_to_sid(PyObject *self, PyObject *args)
309 {
310         struct winbindd_request request;
311         struct winbindd_response response;
312         int id;
313
314         if (!PyArg_ParseTuple(args, "i", &id))
315                 return NULL;
316
317         ZERO_STRUCT(request);
318         ZERO_STRUCT(response);
319
320         request.data.gid = id;
321
322         if (winbindd_request(WINBINDD_GID_TO_SID, &request, &response) 
323             != NSS_STATUS_SUCCESS) {
324                 PyErr_SetString(winbind_error, "lookup failed");
325                 return NULL;            
326         }
327
328         return PyString_FromString(response.data.sid.sid);
329 }
330
331 /* Convert a sid to a uid */
332
333 static PyObject *py_sid_to_uid(PyObject *self, PyObject *args)
334 {
335         struct winbindd_request request;
336         struct winbindd_response response;
337         char *sid;
338
339         if (!PyArg_ParseTuple(args, "s", &sid))
340                 return NULL;
341
342         ZERO_STRUCT(request);
343         ZERO_STRUCT(response);
344
345         fstrcpy(request.data.sid, sid);
346
347         if (winbindd_request(WINBINDD_SID_TO_UID, &request, &response) 
348             != NSS_STATUS_SUCCESS) {
349                 PyErr_SetString(winbind_error, "lookup failed");
350                 return NULL;            
351         }
352
353         return PyInt_FromLong(response.data.uid);
354 }
355
356 /* Convert a sid to a gid */
357
358 static PyObject *py_sid_to_gid(PyObject *self, PyObject *args)
359 {
360         struct winbindd_request request;
361         struct winbindd_response response;
362         char *sid;
363
364         if (!PyArg_ParseTuple(args, "s", &sid))
365                 return NULL;
366
367         ZERO_STRUCT(request);
368         ZERO_STRUCT(response);
369
370         fstrcpy(request.data.sid, sid);
371
372         if (winbindd_request(WINBINDD_SID_TO_GID, &request, &response) 
373             != NSS_STATUS_SUCCESS) {
374                 PyErr_SetString(winbind_error, "lookup failed");
375                 return NULL;            
376         }
377         
378         return PyInt_FromLong(response.data.gid);
379 }
380
381 /*
382  * PAM authentication functions
383  */
384
385 /* Plaintext authentication */
386
387 static PyObject *py_auth_plaintext(PyObject *self, PyObject *args)
388 {
389         struct winbindd_request request;
390         struct winbindd_response response;
391         char *username, *password;
392
393         if (!PyArg_ParseTuple(args, "ss", &username, &password))
394                 return NULL;
395
396         ZERO_STRUCT(request);
397         ZERO_STRUCT(response);
398
399         fstrcpy(request.data.auth.user, username);
400         fstrcpy(request.data.auth.pass, password);
401
402         if (winbindd_request(WINBINDD_PAM_AUTH, &request, &response) 
403             != NSS_STATUS_SUCCESS) {
404                 PyErr_SetString(winbind_error, "lookup failed");
405                 return NULL;            
406         }
407         
408         return PyInt_FromLong(response.data.auth.nt_status);
409 }
410
411 /* Challenge/response authentication */
412
413 static PyObject *py_auth_crap(PyObject *self, PyObject *args, PyObject *kw)
414 {
415         static char *kwlist[] = 
416                 {"username", "password", "use_lm_hash", "use_nt_hash", NULL };
417         struct winbindd_request request;
418         struct winbindd_response response;
419         char *username, *password;
420         int use_lm_hash = 1, use_nt_hash = 1;
421
422         if (!PyArg_ParseTupleAndKeywords(
423                     args, kw, "ss|ii", kwlist, &username, &password, 
424                     &use_lm_hash, &use_nt_hash))
425                 return NULL;
426
427         ZERO_STRUCT(request);
428         ZERO_STRUCT(response);
429
430         fstrcpy(request.data.auth_crap.user, username);
431
432         generate_random_buffer(request.data.auth_crap.chal, 8, False);
433         
434         if (use_lm_hash) {
435                 SMBencrypt((uchar *)password, request.data.auth_crap.chal, 
436                            (uchar *)request.data.auth_crap.lm_resp);
437                 request.data.auth_crap.lm_resp_len = 24;
438         }
439
440         if (use_nt_hash) {
441                 SMBNTencrypt((uchar *)password, request.data.auth_crap.chal,
442                              (uchar *)request.data.auth_crap.nt_resp);
443                 request.data.auth_crap.nt_resp_len = 24;
444         }
445
446         if (winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response) 
447             != NSS_STATUS_SUCCESS) {
448                 PyErr_SetString(winbind_error, "lookup failed");
449                 return NULL;            
450         }
451         
452         return PyInt_FromLong(response.data.auth.nt_status);
453 }
454
455 #if 0                           /* Include when auth_smbd merged to HEAD */
456
457 /* Challenge/response authentication, with secret */
458
459 static PyObject *py_auth_smbd(PyObject *self, PyObject *args, PyObject *kw)
460 {
461         static char *kwlist[] = 
462                 {"username", "password", "use_lm_hash", "use_nt_hash", NULL };
463         struct winbindd_request request;
464         struct winbindd_response response;
465         char *username, *password;
466         int use_lm_hash = 1, use_nt_hash = 1;
467
468         if (!PyArg_ParseTupleAndKeywords(
469                     args, kw, "ss|ii", kwlist, &username, &password, 
470                     &use_lm_hash, &use_nt_hash))
471                 return NULL;
472
473         ZERO_STRUCT(request);
474         ZERO_STRUCT(response);
475
476         fstrcpy(request.data.smbd_auth_crap.user, username);
477
478         generate_random_buffer(request.data.smbd_auth_crap.chal, 8, False);
479         
480         if (use_lm_hash) {
481                 SMBencrypt((uchar *)password, 
482                            request.data.smbd_auth_crap.chal, 
483                            (uchar *)request.data.smbd_auth_crap.lm_resp);
484                 request.data.smbd_auth_crap.lm_resp_len = 24;
485         }
486
487         if (use_nt_hash) {
488                 SMBNTencrypt((uchar *)password, 
489                              request.data.smbd_auth_crap.chal,
490                              (uchar *)request.data.smbd_auth_crap.nt_resp);
491                 request.data.smbd_auth_crap.nt_resp_len = 24;
492         }
493
494         if (!secrets_fetch_trust_account_password(
495                     lp_workgroup(), request.data.smbd_auth_crap.proof, NULL)) {
496                 PyErr_SetString(
497                         winbind_error, "unable to fetch domain secret");
498                 return NULL;
499         }
500
501
502
503         if (winbindd_request(WINBINDD_SMBD_AUTH_CRAP, &request, &response) 
504             != NSS_STATUS_SUCCESS) {
505                 PyErr_SetString(winbind_error, "lookup failed");
506                 return NULL;            
507         }
508         
509         return PyInt_FromLong(response.data.auth.nt_status);
510 }
511
512 #endif /* 0 */
513
514 /* Get user info from name */
515
516 static PyObject *py_getpwnam(PyObject *self, PyObject *args)
517 {
518         struct winbindd_request request;
519         struct winbindd_response response;
520         char *username;
521         PyObject *result;
522
523         if (!PyArg_ParseTuple(args, "s", &username))
524                 return NULL;
525
526         ZERO_STRUCT(request);
527         ZERO_STRUCT(response);
528
529         fstrcpy(request.data.username, username);
530
531         if (winbindd_request(WINBINDD_GETPWNAM, &request, &response) 
532             != NSS_STATUS_SUCCESS) {
533                 PyErr_SetString(winbind_error, "lookup failed");
534                 return NULL;            
535         }
536         
537         if (!py_from_winbind_passwd(&result, &response)) {
538                 result = Py_None;
539                 Py_INCREF(result);
540         }
541
542         return result;
543 }
544
545 /* Get user info from uid */
546
547 static PyObject *py_getpwuid(PyObject *self, PyObject *args)
548 {
549         struct winbindd_request request;
550         struct winbindd_response response;
551         uid_t uid;
552         PyObject *result;
553
554         if (!PyArg_ParseTuple(args, "i", &uid))
555                 return NULL;
556
557         ZERO_STRUCT(request);
558         ZERO_STRUCT(response);
559
560         request.data.uid = uid;
561
562         if (winbindd_request(WINBINDD_GETPWUID, &request, &response) 
563             != NSS_STATUS_SUCCESS) {
564                 PyErr_SetString(winbind_error, "lookup failed");
565                 return NULL;            
566         }
567         
568         if (!py_from_winbind_passwd(&result, &response)) {
569                 result = Py_None;
570                 Py_INCREF(result);
571         }
572
573         return result;
574 }
575
576 /*
577  * Method dispatch table
578  */
579
580 static PyMethodDef winbind_methods[] = {
581
582         { "getpwnam", (PyCFunction)py_getpwnam, METH_VARARGS, "getpwnam(3)" },
583         { "getpwuid", (PyCFunction)py_getpwuid, METH_VARARGS, "getpwuid(3)" },
584
585         /* Name <-> SID conversion */
586
587         { "name_to_sid", (PyCFunction)py_name_to_sid, METH_VARARGS,
588           "name_to_sid(s) -> string\n"
589 "\n"
590 "Return the SID for a name.\n"
591 "\n"
592 "Example:\n"
593 "\n"
594 ">>> winbind.name_to_sid('FOO/Administrator')\n"
595 "'S-1-5-21-406022937-1377575209-526660263-500' " },
596
597         { "sid_to_name", (PyCFunction)py_sid_to_name, METH_VARARGS,
598           "sid_to_name(s) -> string\n"
599 "\n"
600 "Return the name for a SID.\n"
601 "\n"
602 "Example:\n"
603 "\n"
604 ">>> import winbind\n"
605 ">>> winbind.sid_to_name('S-1-5-21-406022937-1377575209-526660263-500')\n"
606 "'FOO/Administrator' " },
607
608         /* Enumerate users/groups */
609
610         { "enum_domain_users", (PyCFunction)py_enum_domain_users, METH_VARARGS,
611           "enum_domain_users() -> list of strings\n"
612 "\n"
613 "Return a list of domain users.\n"
614 "\n"
615 "Example:\n"
616 "\n"
617 ">>> winbind.enum_domain_users()\n"
618 "['FOO/Administrator', 'FOO/anna', 'FOO/Anne Elk', 'FOO/build', \n"
619 "'FOO/foo', 'FOO/foo2', 'FOO/foo3', 'FOO/Guest', 'FOO/user1', \n"
620 "'FOO/whoops-ptang'] " },
621
622         { "enum_domain_groups", (PyCFunction)py_enum_domain_groups, 
623           METH_VARARGS,
624           "enum_domain_groups() -> list of strings\n"
625 "\n"
626 "Return a list of domain groups.\n"
627 "\n"
628 "Example:\n"
629 "\n"
630 ">>> winbind.enum_domain_groups()\n"
631 "['FOO/cows', 'FOO/Domain Admins', 'FOO/Domain Guests', \n"
632 "'FOO/Domain Users'] " },
633
634         /* ID mapping */
635
636         { "uid_to_sid", (PyCFunction)py_uid_to_sid, METH_VARARGS,
637           "uid_to_sid(int) -> string\n"
638 "\n"
639 "Return the SID for a UNIX uid.\n"
640 "\n"
641 "Example:\n"
642 "\n"
643 ">>> winbind.uid_to_sid(10000)   \n"
644 "'S-1-5-21-406022937-1377575209-526660263-500' " },
645
646         { "gid_to_sid", (PyCFunction)py_gid_to_sid, METH_VARARGS,
647           "gid_to_sid(int) -> string\n"
648 "\n"
649 "Return the UNIX gid for a SID.\n"
650 "\n"
651 "Example:\n"
652 "\n"
653 ">>> winbind.gid_to_sid(10001)\n"
654 "'S-1-5-21-406022937-1377575209-526660263-512' " },
655
656         { "sid_to_uid", (PyCFunction)py_sid_to_uid, METH_VARARGS,
657           "sid_to_uid(string) -> int\n"
658 "\n"
659 "Return the UNIX uid for a SID.\n"
660 "\n"
661 "Example:\n"
662 "\n"
663 ">>> winbind.sid_to_uid('S-1-5-21-406022937-1377575209-526660263-500')\n"
664 "10000 " },
665
666         { "sid_to_gid", (PyCFunction)py_sid_to_gid, METH_VARARGS,
667           "sid_to_gid(string) -> int\n"
668 "\n"
669 "Return the UNIX gid corresponding to a SID.\n"
670 "\n"
671 "Example:\n"
672 "\n"
673 ">>> winbind.sid_to_gid('S-1-5-21-406022937-1377575209-526660263-512')\n"
674 "10001 " },
675
676         /* Miscellaneous */
677
678         { "check_secret", (PyCFunction)py_check_secret, METH_VARARGS,
679           "check_secret() -> int\n"
680 "\n"
681 "Check the machine trust account password.  The NT status is returned\n"
682 "with zero indicating success. " },
683
684         { "enum_trust_dom", (PyCFunction)py_enum_trust_dom, METH_VARARGS,
685           "enum_trust_dom() -> list of strings\n"
686 "\n"
687 "Return a list of trusted domains.  The domain the server is a member \n"
688 "of is not included.\n"
689 "\n"
690 "Example:\n"
691 "\n"
692 ">>> winbind.enum_trust_dom()\n"
693 "['NPSD-TEST2', 'SP2NDOM'] " },
694
695         /* PAM authorisation functions */
696
697         { "auth_plaintext", (PyCFunction)py_auth_plaintext, METH_VARARGS,
698           "auth_plaintext(s, s) -> int\n"
699 "\n"
700 "Authenticate a username and password using plaintext authentication.\n"
701 "The NT status code is returned with zero indicating success." },
702
703         { "auth_crap", (PyCFunction)py_auth_crap, METH_VARARGS,
704           "auth_crap(s, s) -> int\n"
705 "\n"
706 "Authenticate a username and password using the challenge/response\n"
707 "protocol.  The NT status code is returned with zero indicating\n"
708 "success." },
709
710 #if 0                           /* Include when smbd_auth merged to HEAD */
711
712         { "auth_smbd", (PyCFunction)py_auth_crap, METH_VARARGS,
713           "auth_smbd(s, s) -> int\n"
714 "\n"
715 "Authenticate a username and password using the challenge/response\n"
716 "protocol but using the domain secret to prove we are root.  The NT \n"
717 "status code is returned with zero indicating success." },
718
719 #endif
720
721         { NULL }
722 };
723
724 static struct const_vals {
725         char *name;
726         uint32 value;
727         char *docstring;
728 } module_const_vals[] = {
729
730         /* Well known RIDs */
731         
732         { "DOMAIN_USER_RID_ADMIN", DOMAIN_USER_RID_ADMIN, 
733           "Well-known RID for Administrator user" },
734
735         { "DOMAIN_USER_RID_GUEST", DOMAIN_USER_RID_GUEST,
736           "Well-known RID for Guest user" },
737
738         { "DOMAIN_GROUP_RID_ADMINS", DOMAIN_GROUP_RID_ADMINS,
739           "Well-known RID for Domain Admins group" },
740
741         { "DOMAIN_GROUP_RID_USERS", DOMAIN_GROUP_RID_USERS,
742           "Well-known RID for Domain Users group" },
743
744         { "DOMAIN_GROUP_RID_GUESTS", DOMAIN_GROUP_RID_GUESTS,
745           "Well-known RID for Domain Guests group" }, 
746         
747         { NULL }
748 };
749
750 static void const_init(PyObject *dict)
751 {
752         struct const_vals *tmp;
753         PyObject *obj;
754
755         for (tmp = module_const_vals; tmp->name; tmp++) {
756                 obj = PyInt_FromLong(tmp->value);
757                 PyDict_SetItemString(dict, tmp->name, obj);
758                 Py_DECREF(obj);
759         }
760 }
761
762 /*
763  * Module initialisation 
764  */
765
766 static char winbind_module__doc__[] =
767 "A python extension to winbind client functions.";
768
769 void initwinbind(void)
770 {
771         PyObject *module, *dict;
772
773         /* Initialise module */
774
775         module = Py_InitModule3("winbind", winbind_methods,
776                                 winbind_module__doc__);
777
778         dict = PyModule_GetDict(module);
779
780         winbind_error = PyErr_NewException("winbind.error", NULL, NULL);
781         PyDict_SetItemString(dict, "error", winbind_error);
782
783         /* Do samba initialisation */
784
785         py_samba_init();
786
787         /* Initialise constants */
788
789         const_init(dict);
790
791         /* Insert configuration dictionary */
792
793         PyDict_SetItemString(dict, "config", py_config_dict());
794 }