Subject: | [PATCH]: Add support for Kerberos 'Services 4 User' protocol extensions to the module |
Hello,
Attached patch implements 'Services 4 User' Kerberos protocol extension functions:
http://k5wiki.kerberos.org/wiki/Projects/Services4User
- S4U2Self - protocol transition.
- S4U2Proxy - constrained delegation.
These extensions are implemented starting with MIT krb5 1.8, I've tested attached
patch on Red Hat Enterprise Linux 6.4 - krb5 1.10, perl-Authen-Krb5 1.9.
Could you consider adding this functionality to the Authen::Krb5 module ?
Best Regards
Jarek
Subject: | Krb5-1.9-S4U.patch |
This patch adds 'Services for User' (S4U) Kerberos protocol
extensions implementation (MIT Krb5 >= 1.10):
http://k5wiki.kerberos.org/wiki/Projects/Services4User
Both S4U2Self (protocol transition) and S4U2Proxy (constrained
delegation) extensions are implemented.
Implementation tested with: Red Hat Enterprise Linux 6.4
(krb5 1.10), perl-Authen-Krb5 1.9 (plus this patch), Microsoft
Active Directory 2008.
13.10.2013 Jaroslaw.Polok@gmail.com
diff -Naur Krb5-1.9/Changes Krb5-1.9-S4U/Changes
--- Krb5-1.9/Changes 2010-01-04 02:31:03.000000000 +0100
+++ Krb5-1.9-S4U/Changes 2013-10-13 13:37:46.835596507 +0200
@@ -1,5 +1,10 @@
Revision history for Perl extension Krb5.
+1.X Add get_credentials_for_user(), get_credentials_for_proxy()
+ S4U2Self/S4U2Proxy implementation (MIT Krb5 >= 1.10)
+ http://k5wiki.kerberos.org/wiki/Projects/Services4User
+ (jaroslaw.polok@gmail.com)
+
1.9 Add Authen::Krb5::Creds object (tom.jones@oucs.ox.ac.uk)
1.8 Fix broken get_in_tkt_with_password implementation (rra@debian.org)
diff -Naur Krb5-1.9/Krb5.pm Krb5-1.9-S4U/Krb5.pm
--- Krb5-1.9/Krb5.pm 2010-01-04 02:38:58.000000000 +0100
+++ Krb5-1.9-S4U/Krb5.pm 2013-10-14 09:35:25.813871166 +0200
@@ -231,6 +231,30 @@
supported in the Perl module. In this module, it's implemented in terms of
krb5_get_init_creds_keytab, krb5_cc_initialize, and krb5_cc_store_cred.
+=item get_credentials_for_user(client, server, cc)
+
+Obtain credentials for user using S4U2Self protocol extension (protocol
+transition) 'client' is a principal object for which you want credentials.
+'server' is a principal which can acquire credentials for 'client'.
+'cc' is a Authen::Krb5::Ccache object representing the current
+credentials cache. Returns a Kerberos error code.
+
+This interface implements S4U2Self protocol extension described at:
+http://k5wiki.kerberos.org/wiki/Projects/Services4User#API
+
+= item get_credentials_for_proxy(client, server, proxy, cc, keytab)
+
+Obtain credentials for user using S4U2Proxy protocol extension
+(constrained delegation). 'client' is a principal object for which
+you want credentials. 'server' is a principal which can acquire
+credentials for 'client' for principal object 'proxy'.
+'cc' is a Authen::Krb5::Ccache object representing the current
+credentials cache. 'keytab' is a keytab object containing valid
+keys for 'server'. Returns a Kerberos error code.
+
+This interface implements S4U2Proxy protocol extension described at:
+http://k5wiki.kerberos.org/wiki/Projects/Services4User#API
+
=item mk_req(auth_context,ap_req_options,service,hostname,in,cc)
Obtains a ticket for a specified service and returns a KRB_AP_REQ message
diff -Naur Krb5-1.9/Krb5.xs Krb5-1.9-S4U/Krb5.xs
--- Krb5-1.9/Krb5.xs 2010-01-04 02:29:19.000000000 +0100
+++ Krb5-1.9-S4U/Krb5.xs 2013-10-14 09:34:07.741554748 +0200
@@ -52,6 +52,8 @@
*/
krb5_error_code krb5_free_krbhst(krb5_context, char * const *);
krb5_error_code krb5_get_krbhst(krb5_context, const krb5_data *, char ***);
+krb5_error_code krb5_get_credentials_for_user(krb5_context, krb5_flags, krb5_ccache, krb5_creds *,krb5_data *, krb5_creds **);
+krb5_error_code krb5_get_credentials_for_proxy(krb5_context, krb5_flags, krb5_ccache, krb5_creds *,krb5_ticket *, krb5_creds **);
/*
* The following three routines implement a "safehouse" for nested Kerberos
@@ -331,6 +333,167 @@
OUTPUT:
RETVAL
+
+Authen::Krb5::Creds
+krb5_get_credentials_for_user(principal_for_user, principal, cc)
+ Authen::Krb5::Principal principal_for_user
+ Authen::Krb5::Principal principal
+ Authen::Krb5::Ccache cc
+
+ PREINIT:
+ krb5_creds creds;
+
+ CODE:
+ memset(&creds, 0, sizeof(creds));
+ creds.client = principal_for_user;
+ creds.server = principal;
+ creds.keyblock.enctype = 0;
+
+ RETVAL = calloc(1, sizeof(krb5_creds));
+ if (RETVAL == NULL) {
+ err = errno;
+ XSRETURN_UNDEF;
+ }
+
+ err = krb5_get_credentials_for_user(context, KRB5_GC_NO_STORE, cc, &creds, NULL, &RETVAL);
+ if (err) {
+ free(RETVAL);
+ XSRETURN_UNDEF;
+ }
+ can_free((SV *)RETVAL);
+
+ OUTPUT:
+ RETVAL
+
+
+Authen::Krb5::Creds
+krb5_get_credentials_for_proxy(principal_for_user, principal, principal_for_proxy, cc, keytab)
+ Authen::Krb5::Principal principal_for_user
+ Authen::Krb5::Principal principal
+ Authen::Krb5::Principal principal_for_proxy
+ Authen::Krb5::Ccache cc
+ Authen::Krb5::Keytab keytab
+
+ PREINIT:
+ krb5_creds creds;
+ krb5_ticket *ticket=NULL;
+ krb5_creds *creds_out;
+
+ char keytab_out_name[20];
+ krb5_keytab keytab_out;
+ krb5_kt_cursor kt_cursor;
+ krb5_keytab_entry kt_entry;
+
+ CODE:
+
+# NOTE: this part of code should not be necessary, unfortunately in MIT krb5
+# krb5_server_decrypt_ticket_keytab() takes into account first(?)
+# principal found in a keytab and fails if that one does not
+# match (for keytabs containing multiple principals), so we make a
+# temporary keytab containg only entries we want as a workaround
+
+ sprintf(keytab_out_name,"MEMORY:Krb5_%06d",getpid());
+
+ err = krb5_kt_resolve(context,keytab_out_name,&keytab_out);
+ if (err) {
+ XSRETURN_UNDEF;
+ }
+
+ err = krb5_kt_start_seq_get(context,keytab,&kt_cursor);
+ if (err) {
+ XSRETURN_UNDEF;
+ }
+
+ while(krb5_kt_next_entry(context,keytab,&kt_entry,&kt_cursor) == 0) {
+ if(krb5_principal_compare(context,kt_entry.principal,principal)) {
+ err=krb5_kt_add_entry(context,keytab_out,&kt_entry);
+ if (err) {
+ XSRETURN_UNDEF;
+ }
+ }
+ }
+
+ if (err) {
+ XSRETURN_UNDEF;
+ }
+
+ err = krb5_kt_end_seq_get(context,keytab,&kt_cursor);
+ if (err) {
+ XSRETURN_UNDEF;
+ }
+
+#
+# NOTE: end of workaround
+#
+ memset(&creds, 0, sizeof(creds));
+ creds.client = principal_for_user;
+ creds.server = principal;
+ creds.keyblock.enctype = 0;
+
+ creds_out = calloc(1, sizeof(krb5_creds));
+ if (creds_out == NULL) {
+ err = errno;
+ XSRETURN_UNDEF;
+ }
+
+ err = krb5_get_credentials_for_user(context, KRB5_GC_NO_STORE, cc, &creds, NULL, &creds_out);
+ if (err) {
+ free(creds_out);
+ XSRETURN_UNDEF;
+ }
+
+ ticket = calloc(1, sizeof(krb5_ticket));
+ if (ticket == NULL) {
+ err = errno;
+ free(creds_out);
+ XSRETURN_UNDEF;
+ }
+
+ err = krb5_decode_ticket(&creds_out->ticket, &ticket);
+ if (err) {
+ free(creds_out);
+ free(ticket);
+ XSRETURN_UNDEF;
+ }
+
+#
+# NOTE: temporary keytab_out used instead of input one (keytab)
+#
+ err = krb5_server_decrypt_ticket_keytab(context, keytab_out, ticket);
+ if (err) {
+ free(creds_out);
+ free(keytab_out_name);
+ free(ticket);
+ XSRETURN_UNDEF;
+ }
+
+ RETVAL = calloc(1, sizeof(krb5_creds));
+ if (RETVAL == NULL) {
+ err = errno;
+ free(creds_out);
+ free(ticket);
+ XSRETURN_UNDEF;
+ }
+
+ free(creds_out);
+ memset(&creds, 0, sizeof(creds));
+ creds.client = ticket->enc_part2->client;
+ creds.server = principal_for_proxy;
+ creds.keyblock.enctype = 0;
+
+ err= krb5_get_credentials_for_proxy(context, KRB5_GC_NO_STORE , cc, &creds, ticket, &RETVAL);
+ if (err) {
+ free(RETVAL);
+ free(ticket);
+ XSRETURN_UNDEF;
+ }
+
+ free(ticket);
+ can_free((SV *)RETVAL);
+
+ OUTPUT:
+ RETVAL
+
Authen::Krb5::Creds
krb5_get_init_creds_password(client, password, service = NULL)
Authen::Krb5::Principal client
diff -Naur Krb5-1.9/sample_S4U Krb5-1.9-S4U/sample_S4U
--- Krb5-1.9/sample_S4U 1970-01-01 01:00:00.000000000 +0100
+++ Krb5-1.9-S4U/sample_S4U 2013-10-14 09:40:40.794157904 +0200
@@ -0,0 +1,37 @@
+#!/usr/local/in/perl -w
+#
+# Just basic usage, no error checking .. etc..
+# to make it work you must use existing principals !
+
+use Authen::Krb5;
+
+Authen::Krb5::init_context();
+Authen::Krb5::init_ets();
+
+$krb5ccache = Authen::Krb5::cc_resolve('/tmp/test.ccache');
+
+$krb5keytab = Authen::Krb5::kt_resolve('/etc/test.keytab');
+
+$krb5princ = Authen::Krb5::parse_name('SERVICE1/hostname.host.domain');
+$krb5ccache->initialize($krb5princ);
+$krb5creds = Authen::Krb5::get_init_creds_keytab($krb5princ,$krb5keytab);
+$krb5ccache->store_cred($krb5creds);
+
+$krb5princ_for_user = Authen::Krb5::parse_name('joeuser');
+
+# S4U2Self
+$krb5creds_for_user= Authen::Krb5::get_credentials_for_user($krb5princ_for_user, $krb5princ, $krb5ccache);
+
+$krb5ccache->store_cred($krb5creds_for_user);
+
+# S4U2Proxy
+$krb5princ_proxy = Authen::Krb5::parse_name('SERVICE2/hostname2.host.domain');
+$krb5creds_for_user_proxy = Authen::Krb5::get_credentials_for_proxy($krb5princ_for_user,$krb5princ,$krb5princ_proxy,$krb5ccache,$krb5keytab);
+
+$krb5ccache->store_cred($krb5creds_for_user_proxy);
+
+
+
+
+
+