2 from afs import pyafs_error
5 cdef import from "afs/ptuser.h":
16 ctypedef char prname[PR_MAXNAMELEN]
19 unsigned int namelist_len
23 unsigned int prlist_len
27 unsigned int idlist_len
38 char name[PR_MAXNAMELEN]
48 char name[PR_MAXNAMELEN]
51 unsigned int prentries_len
52 prlistentries *prentries_val
54 int ubik_PR_NameToID(ubik_client *, afs_int32, namelist *, idlist *)
55 int ubik_PR_IDToName(ubik_client *, afs_int32, idlist *, namelist *)
56 int ubik_PR_INewEntry(ubik_client *, afs_int32, char *, afs_int32, afs_int32)
57 int ubik_PR_NewEntry(ubik_client *, afs_int32, char *, afs_int32, afs_int32, afs_int32 *)
58 int ubik_PR_Delete(ubik_client *, afs_int32, afs_int32)
59 int ubik_PR_AddToGroup(ubik_client *, afs_int32, afs_int32, afs_int32)
60 int ubik_PR_RemoveFromGroup(ubik_client *, afs_int32, afs_int32, afs_int32)
61 int ubik_PR_ListElements(ubik_client *, afs_int32, afs_int32, prlist *, afs_int32 *)
62 int ubik_PR_ListOwned(ubik_client *, afs_int32, afs_int32, prlist *, afs_int32 *)
63 int ubik_PR_ListEntry(ubik_client *, afs_int32, afs_int32, prcheckentry *)
64 int ubik_PR_ChangeEntry(ubik_client *, afs_int32, afs_int32, char *, afs_int32, afs_int32)
65 int ubik_PR_IsAMemberOf(ubik_client *, afs_int32, afs_int32, afs_int32, afs_int32 *)
66 int ubik_PR_ListMax(ubik_client *, afs_int32, afs_int32 *, afs_int32 *)
67 int ubik_PR_SetMax(ubik_client *, afs_int32, afs_int32, afs_int32)
68 int ubik_PR_ListEntries(ubik_client *, afs_int32, afs_int32, afs_int32, prentries *, afs_int32 *)
69 int ubik_PR_SetFieldsEntry(ubik_client *, afs_int32, afs_int32, afs_int32, afs_int32, afs_int32, afs_int32, afs_int32, afs_int32)
71 cdef import from "afs/pterror.h":
75 cdef import from "krb5/krb5.h":
78 struct krb5_principal_data:
81 ctypedef _krb5_context * krb5_context
82 ctypedef krb5_principal_data * krb5_principal
84 ctypedef long krb5_int32
85 ctypedef krb5_int32 krb5_error_code
86 krb5_error_code krb5_init_context(krb5_context *)
87 krb5_error_code krb5_parse_name(krb5_context, char *, krb5_principal *)
88 krb5_error_code krb5_unparse_name(krb5_context, krb5_principal, char **)
89 krb5_error_code krb5_524_conv_principal(krb5_context, krb5_principal, char *, char *, char *)
90 krb5_error_code krb5_425_conv_principal(krb5_context, char *, char *, char *, krb5_principal *)
91 krb5_error_code krb5_get_host_realm(krb5_context, char *, char ***)
92 void krb5_free_host_realm(krb5_context, char **)
93 void krb5_free_principal(krb5_context, krb5_principal)
94 void krb5_free_context(krb5_context)
97 cdef public afs_int32 flags
98 cdef public afs_int32 id
99 cdef public afs_int32 owner
100 cdef public afs_int32 creator
101 cdef public afs_int32 ngroups
102 cdef public afs_int32 nusers
103 cdef public afs_int32 count
104 cdef public object name
108 return '<PTEntry: %s>' % self.name
110 return '<PTEntry: PTS ID %s>' % self.id
112 cdef int _ptentry_from_c(PTEntry p_entry, prcheckentry * c_entry) except -1:
117 p_entry.flags = c_entry.flags
118 p_entry.id = c_entry.id
119 p_entry.owner = c_entry.owner
120 p_entry.creator = c_entry.creator
121 p_entry.ngroups = c_entry.ngroups
122 p_entry.nusers = c_entry.nusers
123 p_entry.count = c_entry.count
124 p_entry.name = c_entry.name
127 cdef int _ptentry_to_c(prcheckentry * c_entry, PTEntry p_entry) except -1:
132 c_entry.flags = p_entry.flags
133 c_entry.id = p_entry.id
134 c_entry.owner = p_entry.owner
135 c_entry.creator = p_entry.creator
136 c_entry.ngroups = p_entry.ngroups
137 c_entry.nusers = p_entry.nusers
138 c_entry.count = p_entry.count
139 strncpy(c_entry.name, p_entry.name, sizeof(c_entry.name))
142 cdef object kname_re = re.compile(r'^([^.].*?)(?<!\\)(?:\.(.*?))?(?<!\\)@([^@]*)$')
144 cdef object kname_parse(fullname):
145 """Parse a krb4-style principal into a name, instance, and realm."""
146 cdef object re_match = kname_re.match(fullname)
150 princ = re_match.groups()
151 return tuple([re.sub(r'\\(.)', r'\1', x) if x else x for x in princ])
153 cdef object kname_unparse(name, inst, realm):
154 """Unparse a name, instance, and realm into a single krb4
156 name = re.sub('r([.\\@])', r'\\\1', name)
157 inst = re.sub('r([.\\@])', r'\\\1', inst)
158 realm = re.sub(r'([\\@])', r'\\\1', realm)
160 return '%s.%s@%s' % (name, inst, realm)
162 return '%s@%s' % (name, realm)
166 A PTS object is essentially a handle to talk to the server in a
169 cell defaults to None. If no argument is passed for cell, PTS
170 connects to the home cell.
172 sec is the security level, an integer from 0 to 3:
173 - 0: unauthenticated connection
174 - 1: try authenticated, then fall back to unauthenticated
175 - 2: fail if an authenticated connection can't be established
176 - 3: same as 2, plus encrypt all traffic to the protection
179 The realm attribute is the Kerberos realm against which this cell
182 cdef ubik_client * client
183 cdef readonly object cell
184 cdef readonly object realm
186 def __cinit__(self, cell=None, sec=1):
188 cdef afsconf_dir *cdir
189 cdef afsconf_cell info
190 cdef krb5_context context
191 cdef char ** hrealms = NULL
193 cdef ktc_principal prin
195 cdef rx_securityClass *sc
196 cdef rx_connection *serverconns[MAXSERVERS]
199 initialize_PT_error_table()
210 raise Exception(code, "Error initializing Rx")
212 cdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH)
215 "Error opening configuration directory (%s): %s" % \
216 (AFSDIR_CLIENT_ETC_DIRPATH, strerror(errno)))
217 code = afsconf_GetCellInfo(cdir, c_cell, "afsprot", &info)
220 code = krb5_init_context(&context)
222 code = krb5_get_host_realm(context, info.hostName[0], &hrealms)
224 self.realm = hrealms[0]
225 krb5_free_host_realm(context, hrealms)
226 krb5_free_context(context)
228 self.cell = info.name
231 strncpy(prin.cell, info.name, sizeof(prin.cell))
233 strncpy(prin.name, "afs", sizeof(prin.name))
235 code = ktc_GetToken(&prin, &token, sizeof(token), NULL);
238 # No really - we wanted authentication
246 sc = rxkad_NewClientSecurityObject(level, &token.sessionKey,
247 token.kvno, token.ticketLen,
251 sc = rxnull_NewClientSecurityObject()
255 memset(serverconns, 0, sizeof(serverconns))
256 for 0 <= i < info.numServers:
257 serverconns[i] = rx_NewConnection(info.hostAddr[i].sin_addr.s_addr,
258 info.hostAddr[i].sin_port,
263 code = ubik_ClientInit(serverconns, &self.client)
266 code = rxs_Release(sc)
268 def __dealloc__(self):
269 ubik_ClientDestroy(self.client)
272 def _NameOrId(self, ident):
274 Given an identifier, convert it to a PTS ID by looking up the
275 name if it's a string, or otherwise just converting it to an
278 if isinstance(ident, basestring):
279 return self._NameToId(ident)
283 def _NameToId(self, name):
285 Converts a user or group to an AFS ID.
289 cdef afs_int32 code, id = ANONYMOUSID
293 lids.idlist_val = NULL
294 lnames.namelist_len = 1
295 lnames.namelist_val = <prname *>malloc(PR_MAXNAMELEN)
296 strncpy(lnames.namelist_val[0], name, PR_MAXNAMELEN)
297 code = ubik_PR_NameToID(self.client, 0, &lnames, &lids)
298 if lids.idlist_val is not NULL:
299 id = lids.idlist_val[0]
300 free(lids.idlist_val)
301 if id == ANONYMOUSID:
306 def _IdToName(self, id):
308 Convert an AFS ID to the name of a user or group.
313 cdef char name[PR_MAXNAMELEN]
316 lids.idlist_val = <afs_int32 *>malloc(sizeof(afs_int32))
317 lids.idlist_val[0] = id
318 lnames.namelist_len = 0
319 lnames.namelist_val = NULL
320 code = ubik_PR_IDToName(self.client, 0, &lids, &lnames)
321 if lnames.namelist_val is not NULL:
322 strncpy(name, lnames.namelist_val[0], sizeof(name))
323 free(lnames.namelist_val)
324 if lids.idlist_val is not NULL:
325 free(lids.idlist_val)
331 def _CreateUser(self, name, id=None):
333 Create a new user in the protection database. If an ID is
334 provided, that one will be used.
338 name = name[:PR_MAXNAMELEN].lower()
344 code = ubik_PR_INewEntry(self.client, 0, name, cid, 0)
346 code = ubik_PR_NewEntry(self.client, 0, name, 0, 0, &cid)
351 def _CreateGroup(self, name, owner, id=None):
353 Create a new group in the protection database. If an ID is
354 provided, that one will be used.
356 cdef afs_int32 code, cid
358 name = name[:PR_MAXNAMELEN].lower()
359 oid = self._NameOrId(owner)
363 code = ubik_PR_INewEntry(self.client, 0, name, cid, oid)
365 code = ubik_PR_NewEntry(self.client, 0, name, PRGRP, oid, &cid)
370 def _Delete(self, ident):
372 Delete the protection database entry with the provided
376 cdef afs_int32 id = self._NameOrId(ident)
378 code = ubik_PR_Delete(self.client, 0, id)
381 def _AddToGroup(self, user, group):
383 Add the given user to the given group.
386 cdef afs_int32 uid = self._NameOrId(user), gid = self._NameOrId(group)
388 code = ubik_PR_AddToGroup(self.client, 0, uid, gid)
391 def _RemoveFromGroup(self, user, group):
393 Remove the given user from the given group.
396 cdef afs_int32 uid = self._NameOrId(user), gid = self._NameOrId(group)
398 code = ubik_PR_RemoveFromGroup(self.client, 0, uid, gid)
401 def _ListMembers(self, ident):
403 Get the membership of an entity.
405 If id is a group, this returns the users that are in that
408 If id is a user, this returns the list of groups that user is
411 This returns a list of PTS IDs.
413 cdef afs_int32 code, over
416 cdef object members = []
418 cdef afs_int32 id = self._NameOrId(ident)
421 alist.prlist_val = NULL
423 code = ubik_PR_ListElements(self.client, 0, id, &alist, &over)
425 if alist.prlist_val is not NULL:
426 for i in range(alist.prlist_len):
427 members.append(alist.prlist_val[i])
428 free(alist.prlist_val)
434 def _ListOwned(self, owner):
436 Get all groups owned by an entity.
438 cdef afs_int32 code, over
441 cdef object owned = []
443 cdef afs_int32 oid = self._NameOrId(owner)
446 alist.prlist_val = NULL
448 code = ubik_PR_ListOwned(self.client, 0, oid, &alist, &over)
450 if alist.prlist_val is not NULL:
451 for i in range(alist.prlist_len):
452 owned.append(alist.prlist_val[i])
453 free(alist.prlist_val)
459 def _ListEntry(self, ident):
461 Load a PTEntry instance with information about the provided
465 cdef prcheckentry centry
466 cdef object entry = PTEntry()
468 cdef afs_int32 id = self._NameOrId(ident)
470 code = ubik_PR_ListEntry(self.client, 0, id, ¢ry)
473 _ptentry_from_c(entry, ¢ry)
476 def _ChangeEntry(self, ident, newname=None, newid=None, newoid=None):
478 Change the name, ID, and/or owner of a PTS entity.
480 For any of newname, newid, and newoid which aren't specified
481 or ar None, the value isn't changed.
484 cdef afs_int32 c_newid = 0, c_newoid = 0
485 cdef char * c_newname
487 cdef afs_int32 id = self._NameOrId(ident)
490 newname = self._IdToName(id)
492 if newid is not None:
494 if newoid is not None:
497 code = ubik_PR_ChangeEntry(self.client, 0, id, c_newname, c_newoid, c_newid)
500 def _IsAMemberOf(self, user, group):
502 Return True if the given user is a member of the given group.
507 cdef afs_int32 uid = self._NameOrId(user), gid = self._NameOrId(group)
509 code = ubik_PR_IsAMemberOf(self.client, 0, uid, gid, &flag)
516 Return a tuple of the maximum user ID and the maximum group
517 ID currently assigned.
519 cdef afs_int32 code, uid, gid
521 code = ubik_PR_ListMax(self.client, 0, &uid, &gid)
526 def _SetMaxUserId(self, id):
528 Set the maximum currently assigned user ID (the next
529 automatically assigned UID will be id + 1)
533 code = ubik_PR_SetMax(self.client, 0, id, 0)
536 def _SetMaxGroupId(self, id):
538 Set the maximum currently assigned user ID (the next
539 automatically assigned UID will be id + 1)
543 code = ubik_PR_SetMax(self.client, 0, id, PRGRP)
546 def _ListEntries(self, users=None, groups=None):
548 Return a list of PTEntry instances representing all entries in
551 Returns just users by default, but can return just users, just
555 cdef afs_int32 flag = 0, startindex = 0, nentries, nextstartindex
556 cdef prentries centries
559 cdef object entries = []
561 if groups is None or users is True:
566 while startindex != -1:
567 centries.prentries_val = NULL
568 centries.prentries_len = 0
571 code = ubik_PR_ListEntries(self.client, 0, flag, startindex, ¢ries, &nextstartindex)
572 if centries.prentries_val is not NULL:
573 for i in range(centries.prentries_len):
575 _ptentry_from_c(e, <prcheckentry *>¢ries.prentries_val[i])
577 free(centries.prentries_val)
580 startindex = nextstartindex
584 def _SetFields(self, ident, access=None, groups=None, users=None):
586 Update the fields for an entry.
588 Valid fields are the privacy flags (access), the group quota
589 (groups), or the "foreign user quota" (users), which doesn't
590 actually seem to do anything, but is included for
594 cdef afs_int32 mask = 0, flags = 0, nusers = 0, ngroups = 0
596 cdef afs_int32 id = self._NameOrId(ident)
598 if access is not None:
600 mask |= PR_SF_ALLBITS
601 if groups is not None:
603 mask |= PR_SF_NGROUPS
604 if users is not None:
606 mask |= PR_SF_NGROUPS
608 code = ubik_PR_SetFieldsEntry(self.client, 0, id, mask, flags, ngroups, nusers, 0, 0)
611 def _AfsToKrb5(self, afs_name):
612 """Convert an AFS principal to a Kerberos v5 one."""
613 cdef krb5_context ctx = NULL
614 cdef krb5_principal princ = NULL
615 cdef krb5_error_code code = 0
616 cdef char * krb5_princ = NULL
617 cdef char *name = NULL, *inst = NULL, *realm = NULL
618 cdef object pname, pinst, prealm
621 pname, prealm = afs_name.rsplit('@', 1)
622 prealm = prealm.upper()
623 krb4_name = '%s@%s' % (pname, prealm)
625 krb4_name = '%s@%s' % (afs_name, self.realm)
627 pname, pinst, prealm = kname_parse(krb4_name)
635 code = krb5_init_context(&ctx)
639 code = krb5_425_conv_principal(ctx, name, inst, realm, &princ)
643 code = krb5_unparse_name(ctx, princ, &krb5_princ)
649 if krb5_princ is not NULL:
652 if princ is not NULL:
653 krb5_free_principal(ctx, princ)
656 krb5_free_context(ctx)
658 def _Krb5ToAfs(self, krb5_name):
659 """Convert a Kerberos v5 principal to an AFS one."""
660 cdef krb5_context ctx = NULL
661 cdef krb5_principal k5_princ = NULL
662 cdef char *k4_name, *k4_inst, *k4_realm
663 cdef object afs_princ
664 cdef object afs_name, afs_realm
666 k4_name = <char *>malloc(40)
668 k4_inst = <char *>malloc(40)
670 k4_realm = <char *>malloc(40)
673 code = krb5_init_context(&ctx)
677 code = krb5_parse_name(ctx, krb5_name, &k5_princ)
681 code = krb5_524_conv_principal(ctx, k5_princ, k4_name, k4_inst, k4_realm)
684 afs_princ = kname_unparse(k4_name, k4_inst, k4_realm)
685 afs_name, afs_realm = afs_princ.rsplit('@', 1)
687 if k4_realm == self.realm:
690 return '%s@%s' % (afs_name, afs_realm.lower())
692 if k5_princ is not NULL:
693 krb5_free_principal(ctx, k5_princ)
696 krb5_free_context(ctx)