Merge from HEAD:
[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_proto.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, PyObject *kw)
416 {
417         static char *kwlist[] = 
418                 {"username", "password", "use_lm_hash", "use_nt_hash", NULL };
419         struct winbindd_request request;
420         struct winbindd_response response;
421         char *username, *password;
422         int use_lm_hash = 1, use_nt_hash = 1;
423
424         if (!PyArg_ParseTupleAndKeywords(
425                     args, kw, "ss|ii", kwlist, &username, &password, 
426                     &use_lm_hash, &use_nt_hash))
427                 return NULL;
428
429         ZERO_STRUCT(request);
430         ZERO_STRUCT(response);
431
432         fstrcpy(request.data.auth_crap.user, username);
433
434         generate_random_buffer(request.data.auth_crap.chal, 8, False);
435         
436         if (use_lm_hash) {
437                 SMBencrypt((uchar *)password, request.data.auth_crap.chal, 
438                            (uchar *)request.data.auth_crap.lm_resp);
439                 request.data.auth_crap.lm_resp_len = 24;
440         }
441
442         if (use_nt_hash) {
443                 SMBNTencrypt((uchar *)password, request.data.auth_crap.chal,
444                              (uchar *)request.data.auth_crap.nt_resp);
445                 request.data.auth_crap.nt_resp_len = 24;
446         }
447
448         if (winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response) 
449             != NSS_STATUS_SUCCESS) {
450                 PyErr_SetString(winbind_error, "lookup failed");
451                 return NULL;            
452         }
453         
454         return PyInt_FromLong(response.data.auth.nt_status);
455 }
456
457 /* Get user info from name */
458
459 static PyObject *py_getpwnam(PyObject *self, PyObject *args)
460 {
461         struct winbindd_request request;
462         struct winbindd_response response;
463         char *username;
464         PyObject *result;
465
466         if (!PyArg_ParseTuple(args, "s", &username))
467                 return NULL;
468
469         ZERO_STRUCT(request);
470         ZERO_STRUCT(response);
471
472         fstrcpy(request.data.username, username);
473
474         if (winbindd_request(WINBINDD_GETPWNAM, &request, &response) 
475             != NSS_STATUS_SUCCESS) {
476                 PyErr_SetString(winbind_error, "lookup failed");
477                 return NULL;            
478         }
479         
480         if (!py_from_winbind_passwd(&result, &response)) {
481                 result = Py_None;
482                 Py_INCREF(result);
483         }
484
485         return result;
486 }
487
488 /* Get user info from uid */
489
490 static PyObject *py_getpwuid(PyObject *self, PyObject *args)
491 {
492         struct winbindd_request request;
493         struct winbindd_response response;
494         uid_t uid;
495         PyObject *result;
496
497         if (!PyArg_ParseTuple(args, "i", &uid))
498                 return NULL;
499
500         ZERO_STRUCT(request);
501         ZERO_STRUCT(response);
502
503         request.data.uid = uid;
504
505         if (winbindd_request(WINBINDD_GETPWUID, &request, &response) 
506             != NSS_STATUS_SUCCESS) {
507                 PyErr_SetString(winbind_error, "lookup failed");
508                 return NULL;            
509         }
510         
511         if (!py_from_winbind_passwd(&result, &response)) {
512                 result = Py_None;
513                 Py_INCREF(result);
514         }
515
516         return result;
517 }
518
519 /*
520  * Method dispatch table
521  */
522
523 static PyMethodDef winbind_methods[] = {
524
525         { "getpwnam", py_getpwnam, METH_VARARGS, "getpwnam(3)" },
526         { "getpwuid", py_getpwuid, METH_VARARGS, "getpwuid(3)" },
527
528         /* Name <-> SID conversion */
529
530         { "name_to_sid", py_name_to_sid, METH_VARARGS,
531           "name_to_sid(s) -> string
532
533 Return the SID for a name.
534
535 Example:
536
537 >>> winbind.name_to_sid('FOO/Administrator')
538 'S-1-5-21-406022937-1377575209-526660263-500' " },
539
540         { "sid_to_name", py_sid_to_name, METH_VARARGS,
541           "sid_to_name(s) -> string
542
543 Return the name for a SID.
544
545 Example:
546
547 >>> import winbind
548 >>> winbind.sid_to_name('S-1-5-21-406022937-1377575209-526660263-500')
549 'FOO/Administrator' " },
550
551         /* Enumerate users/groups */
552
553         { "enum_domain_users", py_enum_domain_users, METH_VARARGS,
554           "enum_domain_users() -> list of strings
555
556 Return a list of domain users.
557
558 Example:
559
560 >>> winbind.enum_domain_users()
561 ['FOO/Administrator', 'FOO/anna', 'FOO/Anne Elk', 'FOO/build', 
562 'FOO/foo', 'FOO/foo2', 'FOO/foo3', 'FOO/Guest', 'FOO/user1', 
563 'FOO/whoops-ptang'] " },
564
565         { "enum_domain_groups", py_enum_domain_groups, METH_VARARGS,
566           "enum_domain_groups() -> list of strings
567
568 Return a list of domain groups.
569
570 Example:
571
572 >>> winbind.enum_domain_groups()
573 ['FOO/cows', 'FOO/Domain Admins', 'FOO/Domain Guests', 
574 'FOO/Domain Users'] " },
575
576         /* ID mapping */
577
578         { "uid_to_sid", py_uid_to_sid, METH_VARARGS,
579           "uid_to_sid(int) -> string
580
581 Return the SID for a UNIX uid.
582
583 Example:
584
585 >>> winbind.uid_to_sid(10000)   
586 'S-1-5-21-406022937-1377575209-526660263-500' " },
587
588         { "gid_to_sid", py_gid_to_sid, METH_VARARGS,
589           "gid_to_sid(int) -> string
590
591 Return the UNIX gid for a SID.
592
593 Example:
594
595 >>> winbind.gid_to_sid(10001)
596 'S-1-5-21-406022937-1377575209-526660263-512' " },
597
598         { "sid_to_uid", py_sid_to_uid, METH_VARARGS,
599           "sid_to_uid(string) -> int
600
601 Return the UNIX uid for a SID.
602
603 Example:
604
605 >>> winbind.sid_to_uid('S-1-5-21-406022937-1377575209-526660263-500')
606 10000 " },
607
608         { "sid_to_gid", py_sid_to_gid, METH_VARARGS,
609           "sid_to_gid(string) -> int
610
611 Return the UNIX gid corresponding to a SID.
612
613 Example:
614
615 >>> winbind.sid_to_gid('S-1-5-21-406022937-1377575209-526660263-512')
616 10001 " },
617
618         /* Miscellaneous */
619
620         { "check_secret", py_check_secret, METH_VARARGS,
621           "check_secret() -> int
622
623 Check the machine trust account password.  The NT status is returned
624 with zero indicating success. " },
625
626         { "enum_trust_dom", py_enum_trust_dom, METH_VARARGS,
627           "enum_trust_dom() -> list of strings
628
629 Return a list of trusted domains.  The domain the server is a member 
630 of is not included.
631
632 Example:
633
634 >>> winbind.enum_trust_dom()
635 ['NPSD-TEST2', 'SP2NDOM'] " },
636
637         /* PAM authorisation functions */
638
639         { "auth_plaintext", py_auth_plaintext, METH_VARARGS,
640           "auth_plaintext(s, s) -> int
641
642 Authenticate a username and password using plaintext authentication.
643 The NT status code is returned with zero indicating success." },
644
645         { "auth_crap", py_auth_crap, METH_VARARGS,
646           "auth_crap(s, s) -> int
647
648 Authenticate a username and password using the challenge/response
649 protocol.  The NT status code is returned with zero indicating
650 success." },
651
652         { NULL }
653 };
654
655 static struct const_vals {
656         char *name;
657         uint32 value;
658         char *docstring;
659 } module_const_vals[] = {
660
661         /* Well known RIDs */
662         
663         { "DOMAIN_USER_RID_ADMIN", DOMAIN_USER_RID_ADMIN, 
664           "Well-known RID for Administrator user" },
665
666         { "DOMAIN_USER_RID_GUEST", DOMAIN_USER_RID_GUEST,
667           "Well-known RID for Guest user" },
668
669         { "DOMAIN_GROUP_RID_ADMINS", DOMAIN_GROUP_RID_ADMINS,
670           "Well-known RID for Domain Admins group" },
671
672         { "DOMAIN_GROUP_RID_USERS", DOMAIN_GROUP_RID_USERS,
673           "Well-known RID for Domain Users group" },
674
675         { "DOMAIN_GROUP_RID_GUESTS", DOMAIN_GROUP_RID_GUESTS,
676           "Well-known RID for Domain Guests group" }, 
677         
678         { NULL }
679 };
680
681 static void const_init(PyObject *dict)
682 {
683         struct const_vals *tmp;
684         PyObject *obj;
685
686         for (tmp = module_const_vals; tmp->name; tmp++) {
687                 obj = PyInt_FromLong(tmp->value);
688                 PyDict_SetItemString(dict, tmp->name, obj);
689                 Py_DECREF(obj);
690         }
691 }
692
693 /*
694  * Module initialisation 
695  */
696
697 static char winbind_module__doc__[] =
698 "A python extension to winbind client functions.";
699
700 void initwinbind(void)
701 {
702         PyObject *module, *dict;
703
704         /* Initialise module */
705
706         module = Py_InitModule3("winbind", winbind_methods,
707                                 winbind_module__doc__);
708
709         dict = PyModule_GetDict(module);
710
711         winbind_error = PyErr_NewException("winbind.error", NULL, NULL);
712         PyDict_SetItemString(dict, "error", winbind_error);
713
714         /* Do samba initialisation */
715
716         py_samba_init();
717
718         /* Initialise constants */
719
720         const_init(dict);
721
722         /* Insert configuration dictionary */
723
724         PyDict_SetItemString(dict, "config", py_config_dict());
725 }