1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
# Authors: John Dennis <jdennis@redhat.com>
#
# Copyright (C) 2012 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import time
import re
import six
import gssapi
from ipalib import errors
if six.PY3:
unicode = str
#-------------------------------------------------------------------------------
# Kerberos error codes
KRB5_CC_NOTFOUND = 2529639053 # Matching credential not found
KRB5_FCC_NOFILE = 2529639107 # No credentials cache found
KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN = 2529638919 # Server not found in Kerberos database
KRB5KRB_AP_ERR_TKT_EXPIRED = 2529638944 # Ticket expired
KRB5_FCC_PERM = 2529639106 # Credentials cache permissions incorrect
KRB5_CC_FORMAT = 2529639111 # Bad format in credentials cache
KRB5_REALM_CANT_RESOLVE = 2529639132 # Cannot resolve network address for KDC in requested realm
krb_ticket_expiration_threshold = 60*5 # number of seconds to accmodate clock skew
krb5_time_fmt = '%m/%d/%y %H:%M:%S'
ccache_name_re = re.compile(r'^((\w+):)?(.+)')
#-------------------------------------------------------------------------------
def krb5_parse_ccache(ccache_name):
'''
Given a Kerberos ccache name parse it into it's scheme and
location components. Currently valid values for the scheme
are:
* FILE
* MEMORY
The scheme is always returned as upper case. If the scheme
does not exist it defaults to FILE.
:parameters:
ccache_name
The name of the Kerberos ccache.
:returns:
A two-tuple of (scheme, ccache)
'''
match = ccache_name_re.search(ccache_name)
if match:
scheme = match.group(2)
location = match.group(3)
if scheme is None:
scheme = 'FILE'
else:
scheme = scheme.upper()
return scheme, location
else:
raise ValueError('Invalid ccache name = "%s"' % ccache_name)
def krb5_unparse_ccache(scheme, name):
return '%s:%s' % (scheme.upper(), name)
def krb5_format_principal_name(user, realm):
'''
Given a Kerberos user principal name and a Kerberos realm
return the Kerberos V5 user principal name.
:parameters:
user
User principal name.
realm
The Kerberos realm the user exists in.
:returns:
Kerberos V5 user principal name.
'''
return '%s@%s' % (user, realm)
def krb5_format_service_principal_name(service, host, realm):
'''
Given a Kerberos service principal name, the host where the
service is running and a Kerberos realm return the Kerberos V5
service principal name.
:parameters:
service
Service principal name.
host
The DNS name of the host where the service is located.
realm
The Kerberos realm the service exists in.
:returns:
Kerberos V5 service principal name.
'''
return '%s/%s@%s' % (service, host, realm)
def krb5_format_tgt_principal_name(realm):
'''
Given a Kerberos realm return the Kerberos V5 TGT name.
:parameters:
realm
The Kerberos realm the TGT exists in.
:returns:
Kerberos V5 TGT name.
'''
return krb5_format_service_principal_name('krbtgt', realm, realm)
def krb5_format_time(timestamp):
'''
Given a UNIX timestamp format it into a string in the same
manner the MIT Kerberos library does. Kerberos timestamps are
always in local time.
:parameters:
timestamp
Unix timestamp
:returns:
formated string
'''
return time.strftime(krb5_time_fmt, time.localtime(timestamp))
def get_credentials(name=None, ccache_name=None):
'''
Obtains GSSAPI credentials with given principal name from ccache. When no
principal name specified, it retrieves the default one for given
credentials cache.
:parameters:
name
gssapi.Name object specifying principal or None for the default
ccache_name
string specifying Kerberos credentials cache name or None for the
default
:returns:
gssapi.Credentials object
'''
store = None
if ccache_name:
store = {'ccache': ccache_name}
try:
return gssapi.Credentials(usage='initiate', name=name, store=store)
except gssapi.exceptions.GSSError as e:
if e.min_code == KRB5_FCC_NOFILE:
raise ValueError('"%s", ccache="%s"' % (e.message, ccache_name))
raise
def get_principal(ccache_name=None):
'''
Gets default principal name from given credentials cache.
:parameters:
ccache_name
string specifying Kerberos credentials cache name or None for the
default
:returns:
Default principal name as string
:raises:
errors.CCacheError if the principal cannot be retrieved from given
ccache
'''
try:
creds = get_credentials(ccache_name=ccache_name)
return unicode(creds.name)
except gssapi.exceptions.GSSError as e:
raise errors.CCacheError(message=unicode(e))
def get_credentials_if_valid(name=None, ccache_name=None):
'''
Obtains GSSAPI credentials with principal name from ccache. When no
principal name specified, it retrieves the default one for given
credentials cache. When the credentials cannot be retrieved or aren't valid
it returns None.
:parameters:
name
gssapi.Name object specifying principal or None for the default
ccache_name
string specifying Kerberos credentials cache name or None for the
default
:returns:
gssapi.Credentials object or None if valid credentials weren't found
'''
try:
creds = get_credentials(name=name, ccache_name=ccache_name)
if creds.lifetime > 0:
return creds
return None
except gssapi.exceptions.ExpiredCredentialsError:
return None
|