1 /*
2  *----------------------------------------------------------------------------
3  *
4  * krb5wrap.cpp
5  *
6  * (C) 2004-2006 Dan Perry (dperry@pppl.gov)
7  * (C) 2006 Brian Elliott Finley (finley@anl.gov)
8  * (C) 2009-2010 Doug Engert (deengert@anl.gov)
9  * (C) 2010 James Y Knight (foom@fuhm.net)
10  * (C) 2010-2013 Ken Dreyer <ktdreyer at ktdreyer.com>
11  * (C) 2012-2017 Mark Proehl <mark at mproehl.net>
12  * (C) 2012-2017 Olaf Flebbe <of at oflebbe.de>
13  * (C) 2013-2017 Daniel Kobras <d.kobras at science-computing.de>
14  *
15     This program is free software; you can redistribute it and/or modify
16     it under the terms of the GNU General Public License as published by
17     the Free Software Foundation; either version 2 of the License, or
18     (at your option) any later version.
19 
20     This program is distributed in the hope that it will be useful,
21     but WITHOUT ANY WARRANTY; without even the implied warranty of
22     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23     GNU General Public License for more details.
24 
25     You should have received a copy of the GNU General Public License
26     along with this program; if not, write to the Free Software
27     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
28  *
29  *-----------------------------------------------------------------------------
30  */
31 #include "msktutil.h"
32 
krb5_error_exit(const char * func,int err_code)33 void krb5_error_exit( const char *func, int err_code) {
34    v_error_exit("error_exit: krb func %s failed: (%s)", func, error_message(err_code));
35 }
36 
krb5_warn(const char * func,int err_code)37 void krb5_warn( const char *func, int err_code) {
38    fprintf( stderr, "Warning: krb func %s failed: (%s)", func, error_message(err_code));
39 }
40 
41 #ifdef HEIMDAL
krb5_free_keytab_entry_contents(krb5_context context,krb5_keytab_entry * entry)42 krb5_error_code krb5_free_keytab_entry_contents(krb5_context context,
43                                                 krb5_keytab_entry *entry)
44 {
45     if (entry) {
46         krb5_free_principal(context, entry->principal);
47         if (entry->keyblock.keyvalue.data) {
48             memset(entry->keyblock.keyvalue.data,
49                    0,
50                    entry->keyblock.keyvalue.length);
51             free(entry->keyblock.keyvalue.data);
52         }
53         return 0;
54     }
55     return -1;
56 }
57 #endif
58 
59 void
initialize_g_context()60 initialize_g_context() {
61     VERBOSE("Creating Kerberos Context");
62     krb5_error_code ret = krb5_init_context(&g_context);
63     if (ret) {
64         krb5_error_exit("krb5_init_context", ret);
65     }
66 }
67 
68 void
destroy_g_context()69 destroy_g_context() {
70     VERBOSE("Destroying Kerberos Context");
71     krb5_free_context(g_context);
72     g_context = 0;
73 }
74 
75 
initialize(KRB5Principal & principal)76 void KRB5CCache::initialize(KRB5Principal &principal)
77 {
78     krb5_error_code ret = krb5_cc_initialize(g_context,
79                                              m_ccache,
80                                              principal.get());
81     if (ret) {
82         throw KRB5Exception("krb5_cc_initialize", ret);
83     }
84 }
85 
86 
store(KRB5Creds & creds)87 void KRB5CCache::store(KRB5Creds &creds)
88 {
89     krb5_error_code ret = krb5_cc_store_cred(g_context,
90                                              m_ccache,
91                                              creds.get());
92     if (ret) {
93         throw KRB5Exception("krb5_cc_store_cred", ret);
94     }
95 }
96 
97 
KRB5Creds(KRB5Principal & principal,KRB5Keytab & keytab,const char * tkt_service)98 KRB5Creds::KRB5Creds(KRB5Principal &principal,
99                      KRB5Keytab &keytab,
100                      const char *tkt_service) : m_creds()
101 {
102     krb5_error_code ret =
103         krb5_get_init_creds_keytab(g_context,
104                                    &m_creds,
105                                    principal.get(),
106                                    keytab.get(),
107                                    0,
108                                    const_cast<char*>(tkt_service), NULL);
109     if (ret) {
110         throw KRB5Exception("krb5_get_init_creds_keytab", ret);
111     }
112 }
113 
114 
KRB5Creds(KRB5Principal & principal,const std::string & password,const char * tkt_service)115 KRB5Creds::KRB5Creds(KRB5Principal &principal,
116                      const std::string &password,
117                      const char *tkt_service) : m_creds()
118 {
119     krb5_error_code ret =
120         krb5_get_init_creds_password(g_context,
121                                      &m_creds,
122                                      principal.get(),
123                                      const_cast<char*>(password.c_str()),
124                                      NULL,
125                                      NULL,
126                                      0,
127                                      const_cast<char*>(tkt_service),
128                                      NULL);
129     if (ret) {
130         throw KRB5Exception("krb5_get_init_creds_keytab", ret);
131     }
132 }
133 
134 
name()135 std::string KRB5Principal::name()
136 {
137     char *principal_string;
138     krb5_error_code ret = krb5_unparse_name(g_context,
139                                             m_princ,
140                                             &principal_string);
141     if (ret) {
142         throw KRB5Exception("krb5_unparse_name", ret);
143     }
144 
145     std::string result(principal_string);
146 
147 #ifdef HEIMDAL
148     krb5_xfree(principal_string);
149 #else
150     krb5_free_unparsed_name(g_context, principal_string);
151 #endif
152 
153     return result;
154 }
155 
156 
addEntry(const KRB5Principal & princ,krb5_kvno kvno,krb5_keyblock & keyblock)157 void KRB5Keytab::addEntry(const KRB5Principal &princ,
158                           krb5_kvno kvno,
159                           krb5_keyblock &keyblock)
160 {
161     krb5_keytab_entry entry;
162 
163     entry.principal = princ.get();
164     entry.vno = kvno;
165 #ifdef HEIMDAL
166     entry.keyblock = keyblock;
167 #else
168     entry.key = keyblock;
169 #endif
170     // avoid duplicate entries
171     (void) krb5_kt_remove_entry(g_context,
172                                 m_keytab,
173                                 &entry);
174     krb5_error_code ret = krb5_kt_add_entry(g_context,
175                                             m_keytab,
176                                             &entry);
177     if (ret) {
178         if (errno != 0) {
179             fprintf(stderr,"Error: Keytab write error: %s!\n", strerror(errno));
180         }
181         throw KRB5Exception("krb5_kt_add_entry failed", ret);
182     }
183 }
184 
addEntry(const KRB5Principal & princ,krb5_kvno kvno,krb5_enctype enctype,const std::string & password,const std::string & salt)185 void KRB5Keytab::addEntry(const KRB5Principal &princ,
186                           krb5_kvno kvno,
187                           krb5_enctype enctype,
188                           const std::string &password,
189                           const std::string &salt)
190 {
191     krb5_keyblock keyblock;
192 
193 #ifdef HEIMDAL
194     krb5_data pass_data;
195     krb5_salt salt_data;
196 
197     salt_data.salttype = KRB5_PW_SALT;
198     salt_data.saltvalue.data = const_cast<char *>(salt.c_str());
199     salt_data.saltvalue.length = salt.length();
200 
201     pass_data.data = const_cast<char *>(password.c_str());
202     pass_data.length = password.length();
203 
204     krb5_error_code ret = krb5_string_to_key_data_salt(g_context,
205                                                        enctype,
206                                                        pass_data,
207                                                        salt_data,
208                                                        &keyblock);
209     if (ret) {
210         throw KRB5Exception("krb5_string_to_key_data_salt", ret);
211     }
212 #else
213     krb5_data salt_data, pass_data;
214     salt_data.data = const_cast<char *>(salt.c_str());
215     salt_data.length = salt.length();
216 
217     pass_data.data = const_cast<char *>(password.c_str());
218     pass_data.length = password.length();
219 
220     krb5_error_code ret = krb5_c_string_to_key(g_context,
221                                                enctype,
222                                                &pass_data,
223                                                &salt_data,
224                                                &keyblock);
225     if (ret) {
226         throw KRB5Exception("krb5_c_string_to_key", ret);
227     }
228 #endif
229 
230     addEntry(princ, kvno, keyblock);
231 }
232 
233 
234 
removeEntry(const KRB5Principal & princ,krb5_kvno kvno,krb5_enctype enctype)235 void KRB5Keytab::removeEntry(const KRB5Principal &princ,
236                              krb5_kvno kvno,
237                              krb5_enctype enctype)
238 {
239     krb5_keytab_entry entry;
240 
241     entry.principal = princ.get();
242     entry.vno = kvno;
243 #ifdef HEIMDAL
244     entry.keyblock.keytype = enctype;
245 #else
246     entry.key.enctype = enctype;
247 #endif
248 
249     krb5_error_code ret = krb5_kt_remove_entry(g_context,
250                                                m_keytab,
251                                                &entry);
252     if (ret) {
253         if (errno != 0) {
254             fprintf(stderr,"Error: Keytab write error: %s!\n", strerror(errno));
255         }
256         throw KRB5Exception("krb5_kt_remove_entry", ret);
257     }
258 }
259 
260 
cursor(KRB5Keytab & keytab)261 KRB5Keytab::cursor::cursor(KRB5Keytab &keytab) : m_keytab(keytab),
262                                                  m_cursor(),
263                                                  m_entry(),
264                                                  m_princ(),
265                                                  m_ok(true)
266 {
267     memset(&m_entry, 0, sizeof(m_entry));
268 
269     krb5_error_code ret = krb5_kt_start_seq_get(g_context,
270                                                 m_keytab.m_keytab,
271                                                 &m_cursor);
272     if (ret) {
273         m_ok = false;
274     }
275 }
276 
277 
~cursor()278 KRB5Keytab::cursor::~cursor()
279 {
280     if (!m_ok) {
281         m_princ.reset_no_free(NULL);
282         return;
283     }
284     krb5_free_keytab_entry_contents(g_context, &m_entry);
285     memset(&m_entry, 0, sizeof(m_entry));
286     /* Tell m_princ to not free its contents! */
287     m_princ.reset_no_free(NULL);
288     krb5_error_code ret = krb5_kt_end_seq_get(g_context,
289                                               m_keytab.m_keytab,
290                                               &m_cursor);
291     if (ret) {
292         krb5_warn("krb5_kt_end_seq_get", ret);
293     }
294 }
295 
reset()296 void KRB5Keytab::cursor::reset()
297 {
298     if (!m_ok) {
299         return;
300     }
301     krb5_error_code ret = krb5_kt_start_seq_get(g_context,
302                                                 m_keytab.m_keytab,
303                                                 &m_cursor);
304     if (ret) {
305         m_ok = false;
306     }
307 }
308 
next()309 bool KRB5Keytab::cursor::next()
310 {
311     if (!m_ok) {
312         return false;
313     }
314     krb5_free_keytab_entry_contents(g_context, &m_entry);
315     memset(&m_entry, 0, sizeof(m_entry));
316     krb5_error_code ret = krb5_kt_next_entry(g_context,
317                                              m_keytab.m_keytab,
318                                              &m_entry,
319                                              &m_cursor);
320     m_princ.reset_no_free(m_entry.principal);
321     return ret == 0;
322 }
323 
324 krb5_context g_context = NULL;
325