Figure out what realm an afs._pts.PTS instance authenticates against.
[invirt/packages/python-afs.git] / afs / _pts.pyx
1 from afs cimport *
2 from afs import pyafs_error
3
4 cdef import from "afs/ptuser.h":
5     enum:
6         PR_MAXNAMELEN
7         PRGRP
8         PRUSERS
9         PRGROUPS
10         ANONYMOUSID
11         PR_SF_ALLBITS
12         PR_SF_NGROUPS
13         PR_SF_NUSERS
14
15     ctypedef char prname[PR_MAXNAMELEN]
16
17     struct namelist:
18         unsigned int namelist_len
19         prname *namelist_val
20
21     struct prlist:
22         unsigned int prlist_len
23         afs_int32 *prlist_val
24
25     struct idlist:
26         unsigned int idlist_len
27         afs_int32 *idlist_val
28
29     struct prcheckentry:
30         afs_int32 flags
31         afs_int32 id
32         afs_int32 owner
33         afs_int32 creator
34         afs_int32 ngroups
35         afs_int32 nusers
36         afs_int32 count
37         char name[PR_MAXNAMELEN]
38
39     struct prlistentries:
40         afs_int32 flags
41         afs_int32 id
42         afs_int32 owner
43         afs_int32 creator
44         afs_int32 ngroups
45         afs_int32 nusers
46         afs_int32 count
47         char name[PR_MAXNAMELEN]
48
49     struct prentries:
50         unsigned int prentries_len
51         prlistentries *prentries_val
52
53     int ubik_PR_NameToID(ubik_client *, afs_int32, namelist *, idlist *)
54     int ubik_PR_IDToName(ubik_client *, afs_int32, idlist *, namelist *)
55     int ubik_PR_INewEntry(ubik_client *, afs_int32, char *, afs_int32, afs_int32)
56     int ubik_PR_NewEntry(ubik_client *, afs_int32, char *, afs_int32, afs_int32, afs_int32 *)
57     int ubik_PR_Delete(ubik_client *, afs_int32, afs_int32)
58     int ubik_PR_AddToGroup(ubik_client *, afs_int32, afs_int32, afs_int32)
59     int ubik_PR_RemoveFromGroup(ubik_client *, afs_int32, afs_int32, afs_int32)
60     int ubik_PR_ListElements(ubik_client *, afs_int32, afs_int32, prlist *, afs_int32 *)
61     int ubik_PR_ListOwned(ubik_client *, afs_int32, afs_int32, prlist *, afs_int32 *)
62     int ubik_PR_ListEntry(ubik_client *, afs_int32, afs_int32, prcheckentry *)
63     int ubik_PR_ChangeEntry(ubik_client *, afs_int32, afs_int32, char *, afs_int32, afs_int32)
64     int ubik_PR_IsAMemberOf(ubik_client *, afs_int32, afs_int32, afs_int32, afs_int32 *)
65     int ubik_PR_ListMax(ubik_client *, afs_int32, afs_int32 *, afs_int32 *)
66     int ubik_PR_SetMax(ubik_client *, afs_int32, afs_int32, afs_int32)
67     int ubik_PR_ListEntries(ubik_client *, afs_int32, afs_int32, afs_int32, prentries *, afs_int32 *)
68     int ubik_PR_SetFieldsEntry(ubik_client *, afs_int32, afs_int32, afs_int32, afs_int32, afs_int32, afs_int32, afs_int32, afs_int32)
69
70 cdef import from "afs/pterror.h":
71     enum:
72         PRNOENT
73
74 cdef import from "krb5/krb5.h":
75     struct _krb5_context:
76         pass
77     struct krb5_principal_data:
78         pass
79
80     ctypedef _krb5_context krb5_context
81     ctypedef krb5_principal_data * krb5_principal
82
83     ctypedef long krb5_int32
84     ctypedef krb5_int32 krb5_error_code
85     krb5_error_code krb5_init_context(krb5_context *)
86     krb5_error_code krb5_parse_name(krb5_context, char *, krb5_principal *)
87     krb5_error_code krb5_unparse_name(krb5_context, krb5_principal, char **)
88     krb5_error_code krb5_524_conv_principal(krb5_context, krb5_principal, char *, char *, char *)
89     krb5_error_code krb5_425_conv_principal(krb5_context, char *, char *, char *, krb5_principal *)
90     krb5_error_code krb5_get_host_realm(krb5_context, char *, char ***)
91     void krb5_free_host_realm(krb5_context, char **)
92     void krb5_free_principal(krb5_context, krb5_principal)
93     void krb5_free_context(krb5_context)
94
95 cdef class PTEntry:
96     cdef public afs_int32 flags
97     cdef public afs_int32 id
98     cdef public afs_int32 owner
99     cdef public afs_int32 creator
100     cdef public afs_int32 ngroups
101     cdef public afs_int32 nusers
102     cdef public afs_int32 count
103     cdef public object name
104
105     def __repr__(self):
106         if self.name != '':
107             return '<PTEntry: %s>' % self.name
108         else:
109             return '<PTEntry: PTS ID %s>' % self.id
110
111 cdef int _ptentry_from_c(PTEntry p_entry, prcheckentry * c_entry) except -1:
112     if p_entry is None:
113         raise TypeError
114         return -1
115
116     p_entry.flags = c_entry.flags
117     p_entry.id = c_entry.id
118     p_entry.owner = c_entry.owner
119     p_entry.creator = c_entry.creator
120     p_entry.ngroups = c_entry.ngroups
121     p_entry.nusers = c_entry.nusers
122     p_entry.count = c_entry.count
123     p_entry.name = c_entry.name
124     return 0
125
126 cdef int _ptentry_to_c(prcheckentry * c_entry, PTEntry p_entry) except -1:
127     if p_entry is None:
128         raise TypeError
129         return -1
130
131     c_entry.flags = p_entry.flags
132     c_entry.id = p_entry.id
133     c_entry.owner = p_entry.owner
134     c_entry.creator = p_entry.creator
135     c_entry.ngroups = p_entry.ngroups
136     c_entry.nusers = p_entry.nusers
137     c_entry.count = p_entry.count
138     strncpy(c_entry.name, p_entry.name, sizeof(c_entry.name))
139     return 0
140
141 cdef class PTS:
142     """
143     A PTS object is essentially a handle to talk to the server in a
144     given cell.
145
146     cell defaults to None. If no argument is passed for cell, PTS
147     connects to the home cell.
148
149     sec is the security level, an integer from 0 to 3:
150       - 0: unauthenticated connection
151       - 1: try authenticated, then fall back to unauthenticated
152       - 2: fail if an authenticated connection can't be established
153       - 3: same as 2, plus encrypt all traffic to the protection
154         server
155
156     The realm attribute is the Kerberos realm against which this cell
157     authenticates.
158     """
159     cdef ubik_client * client
160     cdef readonly object cell
161     cdef readonly object realm
162
163     def __cinit__(self, cell=None, sec=1):
164         cdef afs_int32 code
165         cdef afsconf_dir *cdir
166         cdef afsconf_cell info
167         cdef krb5_context context
168         cdef char ** hrealms = NULL
169         cdef char * c_cell
170         cdef ktc_principal prin
171         cdef ktc_token token
172         cdef rx_securityClass *sc
173         cdef rx_connection *serverconns[MAXSERVERS]
174         cdef int i
175
176         initialize_PT_error_table()
177
178         if cell is None:
179             c_cell = NULL
180         else:
181             c_cell = cell
182
183         self.client = NULL
184
185         code = rx_Init(0)
186         if code != 0:
187             raise Exception(code, "Error initializing Rx")
188
189         cdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH)
190         if cdir is NULL:
191             raise OSError(errno,
192                           "Error opening configuration directory (%s): %s" % \
193                               (AFSDIR_CLIENT_ETC_DIRPATH, strerror(errno)))
194         code = afsconf_GetCellInfo(cdir, c_cell, "afsprot", &info)
195         pyafs_error(code)
196
197         code = krb5_init_context(&context)
198         pyafs_error(code)
199         code = krb5_get_host_realm(context, info.hostName[0], &hrealms)
200         pyafs_error(code)
201         self.realm = hrealms[0]
202         krb5_free_host_realm(context, hrealms)
203         krb5_free_context(context)
204
205         self.cell = info.name
206
207         if sec > 0:
208             strncpy(prin.cell, info.name, sizeof(prin.cell))
209             prin.instance[0] = 0
210             strncpy(prin.name, "afs", sizeof(prin.name))
211
212             code = ktc_GetToken(&prin, &token, sizeof(token), NULL);
213             if code != 0:
214                 if sec >= 2:
215                     # No really - we wanted authentication
216                     pyafs_error(code)
217                 sec = 0
218             else:
219                 if sec == 3:
220                     level = rxkad_crypt
221                 else:
222                     level = rxkad_clear
223                 sc = rxkad_NewClientSecurityObject(level, &token.sessionKey,
224                                                    token.kvno, token.ticketLen,
225                                                    token.ticket)
226
227         if sec == 0:
228             sc = rxnull_NewClientSecurityObject()
229         else:
230             sec = 2
231
232         memset(serverconns, 0, sizeof(serverconns))
233         for 0 <= i < info.numServers:
234             serverconns[i] = rx_NewConnection(info.hostAddr[i].sin_addr.s_addr,
235                                               info.hostAddr[i].sin_port,
236                                               PRSRV,
237                                               sc,
238                                               sec)
239
240         code = ubik_ClientInit(serverconns, &self.client)
241         pyafs_error(code)
242
243         code = rxs_Release(sc)
244
245     def __dealloc__(self):
246         ubik_ClientDestroy(self.client)
247         rx_Finalize()
248
249     def _NameOrId(self, ident):
250         """
251         Given an identifier, convert it to a PTS ID by looking up the
252         name if it's a string, or otherwise just converting it to an
253         integer.
254         """
255         if isinstance(ident, basestring):
256             return self._NameToId(ident)
257         else:
258             return int(ident)
259
260     def _NameToId(self, name):
261         """
262         Converts a user or group to an AFS ID.
263         """
264         cdef namelist lnames
265         cdef idlist lids
266         cdef afs_int32 code, id = ANONYMOUSID
267         name = name.lower()
268
269         lids.idlist_len = 0
270         lids.idlist_val = NULL
271         lnames.namelist_len = 1
272         lnames.namelist_val = <prname *>malloc(PR_MAXNAMELEN)
273         strncpy(lnames.namelist_val[0], name, PR_MAXNAMELEN)
274         code = ubik_PR_NameToID(self.client, 0, &lnames, &lids)
275         if lids.idlist_val is not NULL:
276             id = lids.idlist_val[0]
277             free(lids.idlist_val)
278         if id == ANONYMOUSID:
279             code = PRNOENT
280         pyafs_error(code)
281         return id
282
283     def _IdToName(self, id):
284         """
285         Convert an AFS ID to the name of a user or group.
286         """
287         cdef namelist lnames
288         cdef idlist lids
289         cdef afs_int32 code
290         cdef char name[PR_MAXNAMELEN]
291
292         lids.idlist_len = 1
293         lids.idlist_val = <afs_int32 *>malloc(sizeof(afs_int32))
294         lids.idlist_val[0] = id
295         lnames.namelist_len = 0
296         lnames.namelist_val = NULL
297         code = ubik_PR_IDToName(self.client, 0, &lids, &lnames)
298         if lnames.namelist_val is not NULL:
299             strncpy(name, lnames.namelist_val[0], sizeof(name))
300             free(lnames.namelist_val)
301         if lids.idlist_val is not NULL:
302             free(lids.idlist_val)
303         if name == str(id):
304             code = PRNOENT
305         pyafs_error(code)
306         return name
307
308     def _CreateUser(self, name, id=None):
309         """
310         Create a new user in the protection database. If an ID is
311         provided, that one will be used.
312         """
313         cdef afs_int32 code
314         cdef afs_int32 cid
315         name = name[:PR_MAXNAMELEN].lower()
316
317         if id is not None:
318             cid = id
319
320         if id is not None:
321             code = ubik_PR_INewEntry(self.client, 0, name, cid, 0)
322         else:
323             code = ubik_PR_NewEntry(self.client, 0, name, 0, 0, &cid)
324
325         pyafs_error(code)
326         return cid
327
328     def _CreateGroup(self, name, owner, id=None):
329         """
330         Create a new group in the protection database. If an ID is
331         provided, that one will be used.
332         """
333         cdef afs_int32 code, cid
334
335         name = name[:PR_MAXNAMELEN].lower()
336         oid = self._NameOrId(owner)
337
338         if id is not None:
339             cid = id
340             code = ubik_PR_INewEntry(self.client, 0, name, cid, oid)
341         else:
342             code = ubik_PR_NewEntry(self.client, 0, name, PRGRP, oid, &cid)
343
344         pyafs_error(code)
345         return cid
346
347     def _Delete(self, ident):
348         """
349         Delete the protection database entry with the provided
350         identifier.
351         """
352         cdef afs_int32 code
353         cdef afs_int32 id = self._NameOrId(ident)
354
355         code = ubik_PR_Delete(self.client, 0, id)
356         pyafs_error(code)
357
358     def _AddToGroup(self, user, group):
359         """
360         Add the given user to the given group.
361         """
362         cdef afs_int32 code
363         cdef afs_int32 uid = self._NameOrId(user), gid = self._NameOrId(group)
364
365         code = ubik_PR_AddToGroup(self.client, 0, uid, gid)
366         pyafs_error(code)
367
368     def _RemoveFromGroup(self, user, group):
369         """
370         Remove the given user from the given group.
371         """
372         cdef afs_int32 code
373         cdef afs_int32 uid = self._NameOrId(user), gid = self._NameOrId(group)
374
375         code = ubik_PR_RemoveFromGroup(self.client, 0, uid, gid)
376         pyafs_error(code)
377
378     def _ListMembers(self, ident):
379         """
380         Get the membership of an entity.
381
382         If id is a group, this returns the users that are in that
383         group.
384
385         If id is a user, this returns the list of groups that user is
386         on.
387
388         This returns a list of PTS IDs.
389         """
390         cdef afs_int32 code, over
391         cdef prlist alist
392         cdef int i
393         cdef object members = []
394
395         cdef afs_int32 id = self._NameOrId(ident)
396
397         alist.prlist_len = 0
398         alist.prlist_val = NULL
399
400         code = ubik_PR_ListElements(self.client, 0, id, &alist, &over)
401
402         if alist.prlist_val is not NULL:
403             for i in range(alist.prlist_len):
404                 members.append(alist.prlist_val[i])
405             free(alist.prlist_val)
406
407         pyafs_error(code)
408
409         return members
410
411     def _ListOwned(self, owner):
412         """
413         Get all groups owned by an entity.
414         """
415         cdef afs_int32 code, over
416         cdef prlist alist
417         cdef int i
418         cdef object owned = []
419
420         cdef afs_int32 oid = self._NameOrId(owner)
421
422         alist.prlist_len = 0
423         alist.prlist_val = NULL
424
425         code = ubik_PR_ListOwned(self.client, 0, oid, &alist, &over)
426
427         if alist.prlist_val is not NULL:
428             for i in range(alist.prlist_len):
429                 owned.append(alist.prlist_val[i])
430             free(alist.prlist_val)
431
432         pyafs_error(code)
433
434         return owned
435
436     def _ListEntry(self, ident):
437         """
438         Load a PTEntry instance with information about the provided
439         entity.
440         """
441         cdef afs_int32 code
442         cdef prcheckentry centry
443         cdef object entry = PTEntry()
444
445         cdef afs_int32 id = self._NameOrId(ident)
446
447         code = ubik_PR_ListEntry(self.client, 0, id, &centry)
448         pyafs_error(code)
449
450         _ptentry_from_c(entry, &centry)
451         return entry
452
453     def _ChangeEntry(self, ident, newname=None, newid=None, newoid=None):
454         """
455         Change the name, ID, and/or owner of a PTS entity.
456
457         For any of newname, newid, and newoid which aren't specified
458         or ar None, the value isn't changed.
459         """
460         cdef afs_int32 code
461         cdef afs_int32 c_newid = 0, c_newoid = 0
462         cdef char * c_newname
463
464         cdef afs_int32 id = self._NameOrId(ident)
465
466         if newname is None:
467             newname = self._IdToName(id)
468         c_newname = newname
469         if newid is not None:
470             c_newid = newid
471         if newoid is not None:
472             c_newoid = newoid
473
474         code = ubik_PR_ChangeEntry(self.client, 0, id, c_newname, c_newoid, c_newid)
475         pyafs_error(code)
476
477     def _IsAMemberOf(self, user, group):
478         """
479         Return True if the given user is a member of the given group.
480         """
481         cdef afs_int32 code
482         cdef afs_int32 flag
483
484         cdef afs_int32 uid = self._NameOrId(user), gid = self._NameOrId(group)
485
486         code = ubik_PR_IsAMemberOf(self.client, 0, uid, gid, &flag)
487         pyafs_error(code)
488
489         return bool(flag)
490
491     def _ListMax(self):
492         """
493         Return a tuple of the maximum user ID and the maximum group
494         ID currently assigned.
495         """
496         cdef afs_int32 code, uid, gid
497
498         code = ubik_PR_ListMax(self.client, 0, &uid, &gid)
499         pyafs_error(code)
500
501         return (uid, gid)
502
503     def _SetMaxUserId(self, id):
504         """
505         Set the maximum currently assigned user ID (the next
506         automatically assigned UID will be id + 1)
507         """
508         cdef afs_int32 code
509
510         code = ubik_PR_SetMax(self.client, 0, id, 0)
511         pyafs_error(code)
512
513     def _SetMaxGroupId(self, id):
514         """
515         Set the maximum currently assigned user ID (the next
516         automatically assigned UID will be id + 1)
517         """
518         cdef afs_int32 code
519
520         code = ubik_PR_SetMax(self.client, 0, id, PRGRP)
521         pyafs_error(code)
522
523     def _ListEntries(self, users=None, groups=None):
524         """
525         Return a list of PTEntry instances representing all entries in
526         the PRDB.
527
528         Returns just users by default, but can return just users, just
529         groups, or both.
530         """
531         cdef afs_int32 code
532         cdef afs_int32 flag = 0, startindex = 0, nentries, nextstartindex
533         cdef prentries centries
534         cdef unsigned int i
535
536         cdef object entries = []
537
538         if groups is None or users is True:
539             flag |= PRUSERS
540         if groups:
541             flag |= PRGROUPS
542
543         while startindex != -1:
544             centries.prentries_val = NULL
545             centries.prentries_len = 0
546             nextstartindex = -1
547
548             code = ubik_PR_ListEntries(self.client, 0, flag, startindex, &centries, &nextstartindex)
549             if centries.prentries_val is not NULL:
550                 for i in range(centries.prentries_len):
551                     e = PTEntry()
552                     _ptentry_from_c(e, <prcheckentry *>&centries.prentries_val[i])
553                     entries.append(e)
554                 free(centries.prentries_val)
555             pyafs_error(code)
556
557             startindex = nextstartindex
558
559         return entries
560
561     def _SetFields(self, ident, access=None, groups=None, users=None):
562         """
563         Update the fields for an entry.
564
565         Valid fields are the privacy flags (access), the group quota
566         (groups), or the "foreign user quota" (users), which doesn't
567         actually seem to do anything, but is included for
568         completeness.
569         """
570         cdef afs_int32 code
571         cdef afs_int32 mask = 0, flags = 0, nusers = 0, ngroups = 0
572
573         cdef afs_int32 id = self._NameOrId(ident)
574
575         if access is not None:
576             flags = access
577             mask |= PR_SF_ALLBITS
578         if groups is not None:
579             ngroups = groups
580             mask |= PR_SF_NGROUPS
581         if users is not None:
582             nusers = users
583             mask |= PR_SF_NGROUPS
584
585         code = ubik_PR_SetFieldsEntry(self.client, 0, id, mask, flags, ngroups, nusers, 0, 0)
586         pyafs_error(code)