diff --git a/appl/ftp/ftp/gssapi.c b/appl/ftp/ftp/gssapi.c index 4d1669d8e..6b1378449 100644 --- a/appl/ftp/ftp/gssapi.c +++ b/appl/ftp/ftp/gssapi.c @@ -45,9 +45,9 @@ RCSID("$Id$"); int ftp_do_gss_bindings = 0; int ftp_do_gss_delegate = 1; -struct gss_data { +struct gssapi_data { gss_ctx_id_t context_hdl; - char *client_name; + gss_name_t client_name; gss_cred_id_t delegated_cred_handle; void *mech_data; }; @@ -55,7 +55,7 @@ struct gss_data { static int gss_init(void *app_data) { - struct gss_data *d = app_data; + struct gssapi_data *d = app_data; d->context_hdl = GSS_C_NO_CONTEXT; d->delegated_cred_handle = GSS_C_NO_CREDENTIAL; #if defined(FTP_SERVER) @@ -85,7 +85,7 @@ gss_decode(void *app_data, void *buf, int len, int level) gss_buffer_desc input, output; gss_qop_t qop_state; int conf_state; - struct gss_data *d = app_data; + struct gssapi_data *d = app_data; size_t ret_len; input.length = len; @@ -117,7 +117,7 @@ gss_encode(void *app_data, void *from, int length, int level, void **to) OM_uint32 maj_stat, min_stat; gss_buffer_desc input, output; int conf_state; - struct gss_data *d = app_data; + struct gssapi_data *d = app_data; input.length = length; input.value = from; @@ -173,7 +173,7 @@ gss_adat(void *app_data, void *buf, size_t len) gss_buffer_desc input_token, output_token; OM_uint32 maj_stat, min_stat; gss_name_t client_name; - struct gss_data *d = app_data; + struct gssapi_data *d = app_data; gss_channel_bindings_t bindings; if (ftp_do_gss_bindings) { @@ -219,32 +219,8 @@ gss_adat(void *app_data, void *buf, size_t len) gss_release_buffer(&min_stat, &output_token); } if(maj_stat == GSS_S_COMPLETE){ - char *name; - gss_buffer_desc export_name; - gss_OID oid; - - maj_stat = gss_display_name(&min_stat, client_name, - &export_name, &oid); - if(maj_stat != 0) { - reply(500, "Error displaying name"); - goto out; - } - /* XXX kerberos */ - if(oid != GSS_KRB5_NT_PRINCIPAL_NAME) { - reply(500, "OID not kerberos principal name"); - gss_release_buffer(&min_stat, &export_name); - goto out; - } - name = malloc(export_name.length + 1); - if(name == NULL) { - reply(500, "Out of memory"); - gss_release_buffer(&min_stat, &export_name); - goto out; - } - memcpy(name, export_name.value, export_name.length); - name[export_name.length] = '\0'; - gss_release_buffer(&min_stat, &export_name); - d->client_name = name; + d->client_name = client_name; + client_name = GSS_C_NO_NAME; if(p) reply(235, "ADAT=%s", p); else @@ -279,12 +255,12 @@ gss_adat(void *app_data, void *buf, size_t len) return 0; } -int gss_userok(void*, char*); -int gss_session(void*, char*); +int gssapi_userok(void*, char*); +int gssapi_session(void*, char*); struct sec_server_mech gss_server_mech = { "GSSAPI", - sizeof(struct gss_data), + sizeof(struct gssapi_data), gss_init, /* init */ NULL, /* end */ gss_check_prot, @@ -296,8 +272,8 @@ struct sec_server_mech gss_server_mech = { gss_adat, NULL, /* pbsz */ NULL, /* ccc */ - gss_userok, - gss_session + gssapi_userok, + gssapi_session }; #else /* FTP_SERVER */ @@ -357,7 +333,7 @@ gss_auth(void *app_data, char *host) char *p; int n; gss_channel_bindings_t bindings; - struct gss_data *d = app_data; + struct gssapi_data *d = app_data; OM_uint32 mech_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG; const char *knames[] = { "ftp", "host", NULL }, **kname = knames; @@ -522,7 +498,7 @@ gss_auth(void *app_data, char *host) struct sec_client_mech gss_client_mech = { "GSSAPI", - sizeof(struct gss_data), + sizeof(struct gssapi_data), gss_init, gss_auth, NULL, /* end */ diff --git a/appl/ftp/ftpd/gss_userok.c b/appl/ftp/ftpd/gss_userok.c index d65c581a9..6031b52a8 100644 --- a/appl/ftp/ftpd/gss_userok.c +++ b/appl/ftp/ftpd/gss_userok.c @@ -33,122 +33,43 @@ #include "ftpd_locl.h" #include -#include -#include - -/* XXX a bit too much of krb5 dependency here... - What is the correct way to do this? - */ - -struct gss_krb5_data { - krb5_context context; -}; /* XXX sync with gssapi.c */ -struct gss_data { +struct gssapi_data { gss_ctx_id_t context_hdl; - char *client_name; + gss_name_t client_name; gss_cred_id_t delegated_cred_handle; void *mech_data; }; -int gss_userok(void*, char*); /* to keep gcc happy */ -int gss_session(void*, char*); /* to keep gcc happy */ +int gssapi_userok(void*, char*); /* to keep gcc happy */ +int gssapi_session(void*, char*); /* to keep gcc happy */ int -gss_userok(void *app_data, char *username) +gssapi_userok(void *app_data, char *username) { - struct gss_data *data = app_data; - krb5_error_code ret; - krb5_principal client; - struct gss_krb5_data *kdata; + struct gssapi_data *data = app_data; - kdata = calloc(1, sizeof(struct gss_krb5_data)); - if (kdata == NULL) - return 1; - data->mech_data = kdata; - - ret = krb5_init_context(&(kdata->context)); - if (ret) { - free(kdata); - return 1; - } - - ret = krb5_parse_name(kdata->context, data->client_name, &client); - if(ret) { - krb5_free_context(kdata->context); - free(kdata); - return 1; - } - ret = krb5_kuserok(kdata->context, client, username); - if (!ret) { - krb5_free_principal(kdata->context, client); - krb5_free_context(kdata->context); - free(kdata); - return 1; - } - - ret = 0; - krb5_free_principal(kdata->context, client); - return ret; + /* Yes, this logic really is inverted. */ + return !gss_userok(data->client_name, username); } int -gss_session(void *app_data, char *username) +gssapi_session(void *app_data, char *username) { - struct gss_data *data = app_data; - krb5_error_code ret; - OM_uint32 minor_status; - struct gss_krb5_data *kdata; + struct gssapi_data *data = app_data; + OM_uint32 major, minor; + int ret = 0; - ret = 0; - - kdata = (struct gss_krb5_data *)(data->mech_data); - - /* more of krb-depend stuff :-( */ - /* gss_add_cred() ? */ if (data->delegated_cred_handle != GSS_C_NO_CREDENTIAL) { - krb5_ccache ccache = NULL; - const char* ticketfile; - struct passwd *kpw; - - ret = krb5_cc_new_unique(kdata->context, NULL, NULL, &ccache); - if (ret) - goto fail; - - ticketfile = krb5_cc_get_name(kdata->context, ccache); - - ret = gss_krb5_copy_ccache(&minor_status, - data->delegated_cred_handle, - ccache); - if (ret) { - ret = 0; - goto fail; - } - - do_destroy_tickets = 1; - - kpw = getpwnam(username); - - if (kpw == NULL) { - unlink(ticketfile); - ret = 1; - goto fail; - } - - chown (ticketfile, kpw->pw_uid, kpw->pw_gid); - - if (asprintf(&k5ccname, "FILE:%s", ticketfile) != -1) { - esetenv ("KRB5CCNAME", k5ccname, 1); - } + major = gss_store_cred(&minor, data->delegated_cred_handle, + GSS_C_INITIATE, GSS_C_NO_OID, + 1, 1, NULL, NULL); + if (GSS_ERROR(major)) + ret = 1; afslog(NULL, 1); - fail: - if (ccache) - krb5_cc_close(kdata->context, ccache); } - gss_release_cred(&minor_status, &data->delegated_cred_handle); - krb5_free_context(kdata->context); - free(kdata); + gss_release_cred(&minor, &data->delegated_cred_handle); return ret; } diff --git a/appl/test/common.c b/appl/test/common.c index 9ac880550..dcb785efd 100644 --- a/appl/test/common.c +++ b/appl/test/common.c @@ -43,12 +43,14 @@ krb5_keytab keytab; char *service = SERVICE; char *mech = "krb5"; int fork_flag; +char *password = NULL; static struct getargs args[] = { { "port", 'p', arg_string, &port_str, "port to listen to", "port" }, { "service", 's', arg_string, &service, "service to use", "service" }, { "keytab", 'k', arg_string, &keytab_str, "keytab to use", "keytab" }, { "mech", 'm', arg_string, &mech, "gssapi mech to use", "mech" }, + { "password", 'P', arg_string, &password, "password to use", "password" }, { "fork", 'f', arg_flag, &fork_flag, "do fork" }, { "help", 'h', arg_flag, &help_flag }, { "version", 0, arg_flag, &version_flag } diff --git a/appl/test/gssapi_client.c b/appl/test/gssapi_client.c index e7682195d..f0b85f9bb 100644 --- a/appl/test/gssapi_client.c +++ b/appl/test/gssapi_client.c @@ -94,6 +94,8 @@ do_trans (int sock, gss_ctx_id_t context_hdl) return 0; } +extern char *password; + static int proto (int sock, const char *hostname, const char *service) { @@ -102,6 +104,7 @@ proto (int sock, const char *hostname, const char *service) int context_established = 0; gss_ctx_id_t context_hdl = GSS_C_NO_CONTEXT; + gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; gss_buffer_desc real_input_token, real_output_token; gss_buffer_t input_token = &real_input_token, output_token = &real_output_token; @@ -130,6 +133,26 @@ proto (int sock, const char *hostname, const char *service) gss_err (1, min_stat, "Error importing name `%s@%s':\n", service, hostname); + if (password) { + gss_buffer_desc pw; + + pw.value = password; + pw.length = strlen(password); + + maj_stat = gss_acquire_cred_with_password(&min_stat, + GSS_C_NO_NAME, + &pw, + GSS_C_INDEFINITE, + GSS_C_NO_OID_SET, + GSS_C_INITIATE, + &cred, + NULL, + NULL); + if (GSS_ERROR(maj_stat)) + gss_err (1, min_stat, + "Error acquiring initiator credentials"); + } + addrlen = sizeof(local); if (getsockname (sock, (struct sockaddr *)&local, &addrlen) < 0 || addrlen != sizeof(local)) @@ -172,7 +195,7 @@ proto (int sock, const char *hostname, const char *service) while(!context_established) { maj_stat = gss_init_sec_context(&min_stat, - GSS_C_NO_CREDENTIAL, + cred, &context_hdl, server, mech_oid, diff --git a/appl/test/gssapi_server.c b/appl/test/gssapi_server.c index 0c622796e..3c6654f57 100644 --- a/appl/test/gssapi_server.c +++ b/appl/test/gssapi_server.c @@ -300,6 +300,7 @@ doit (int port, const char *service) int sock, sock2; struct sockaddr_in my_addr; int one = 1; + int ret; sock = socket (AF_INET, SOCK_STREAM, 0); if (sock < 0) @@ -317,14 +318,17 @@ doit (int port, const char *service) if (bind (sock, (struct sockaddr *)&my_addr, sizeof(my_addr)) < 0) err (1, "bind"); - if (listen (sock, 1) < 0) - err (1, "listen"); + while (1) { + if (listen (sock, 1) < 0) + err (1, "listen"); - sock2 = accept (sock, NULL, NULL); - if (sock2 < 0) - err (1, "accept"); + sock2 = accept (sock, NULL, NULL); + if (sock2 < 0) + err (1, "accept"); - return proto (sock2, service); + ret = proto (sock2, service); + } + return ret; } int @@ -334,3 +338,4 @@ main(int argc, char **argv) int port = server_setup(&context, argc, argv); return doit (port, service); } + diff --git a/autogen.sh b/autogen.sh index c3facbf5c..8f6e35eed 100755 --- a/autogen.sh +++ b/autogen.sh @@ -2,4 +2,7 @@ # to really generate all files you need to run "make distcheck" in a # object tree, but this will do if you have all parts of the required # tool-chain installed -autoreconf -f -i || { echo "autoreconf failed: $?"; exit 1; } +PATH=/usr/local/bin:$PATH +/usr/local/bin/autoreconf -f -i || { echo "autoreconf failed: $?"; exit 1; } +find . -name '*-private.h' -delete +find . -name '*-protos.h' -delete diff --git a/lib/gssapi/Makefile.am b/lib/gssapi/Makefile.am index 5f04e4a58..b6ed90ce4 100644 --- a/lib/gssapi/Makefile.am +++ b/lib/gssapi/Makefile.am @@ -59,6 +59,7 @@ krb5src = \ krb5/inquire_mechs_for_name.c \ krb5/inquire_names_for_mech.c \ krb5/inquire_sec_context_by_oid.c \ + krb5/pname_to_uid.c \ krb5/process_context_token.c \ krb5/prf.c \ krb5/release_buffer.c \ @@ -70,6 +71,7 @@ krb5src = \ krb5/set_sec_context_option.c \ krb5/ticket_flags.c \ krb5/unwrap.c \ + krb5/authorize_localname.c \ krb5/verify_mic.c \ krb5/wrap.c @@ -83,6 +85,7 @@ mechsrc = \ mech/gss_acquire_cred_ext.c \ mech/gss_acquire_cred_with_password.c \ mech/gss_add_cred.c \ + mech/gss_add_cred_with_password.c \ mech/gss_add_oid_set_member.c \ mech/gss_aeap.c \ mech/gss_buffer_set.c \ @@ -123,6 +126,7 @@ mechsrc = \ mech/gss_oid.c \ mech/gss_oid_equal.c \ mech/gss_oid_to_str.c \ + mech/gss_pname_to_uid.c \ mech/gss_process_context_token.c \ mech/gss_pseudo_random.c \ mech/gss_release_buffer.c \ @@ -139,6 +143,7 @@ mechsrc = \ mech/gss_test_oid_set_member.c \ mech/gss_unseal.c \ mech/gss_unwrap.c \ + mech/gss_authorize_localname.c \ mech/gss_utils.c \ mech/gss_verify.c \ mech/gss_verify_mic.c \ diff --git a/lib/gssapi/gssapi/gssapi.h b/lib/gssapi/gssapi/gssapi.h index 1fdcc3fb6..84179fbc1 100644 --- a/lib/gssapi/gssapi/gssapi.h +++ b/lib/gssapi/gssapi/gssapi.h @@ -446,6 +446,11 @@ extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_c_nt_export_name_oid_desc; #define GSS_S_NAME_NOT_MN (18ul << GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_BAD_MECH_ATTR (19ul << GSS_C_ROUTINE_ERROR_OFFSET) +/* + * Apparently awating spec fix. + */ +#define GSS_S_CRED_UNAVAIL GSS_S_FAILURE + /* * Supplementary info bits: */ @@ -1021,6 +1026,56 @@ gss_display_mech_attr(OM_uint32 * minor_status, gss_buffer_t short_desc, gss_buffer_t long_desc); +/* + * Solaris compat + */ + +GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_acquire_cred_with_password + (OM_uint32 * /*minor_status*/, + const gss_name_t /*desired_name*/, + const gss_buffer_t /*password*/, + OM_uint32 /*time_req*/, + const gss_OID_set /*desired_mechs*/, + gss_cred_usage_t /*cred_usage*/, + gss_cred_id_t * /*output_cred_handle*/, + gss_OID_set * /*actual_mechs*/, + OM_uint32 * /*time_rec*/ + ); + +GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_add_cred_with_password ( + OM_uint32 * /*minor_status*/, + const gss_cred_id_t /*input_cred_handle*/, + const gss_name_t /*desired_name*/, + const gss_OID /*desired_mech*/, + const gss_buffer_t /*password*/, + gss_cred_usage_t /*cred_usage*/, + OM_uint32 /*initiator_time_req*/, + OM_uint32 /*acceptor_time_req*/, + gss_cred_id_t * /*output_cred_handle*/, + gss_OID_set * /*actual_mechs*/, + OM_uint32 * /*initiator_time_rec*/, + OM_uint32 * /*acceptor_time_rec*/ + ); + +GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL +gss_pname_to_uid( + OM_uint32 *minor, + const gss_name_t name, + const gss_OID mech_type, + uid_t *uidOut); + +GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL +gss_authorize_localname( + OM_uint32 *minor, + const gss_name_t name, + const gss_name_t user); + +GSSAPI_LIB_FUNCTION int GSSAPI_LIB_CALL +gss_userok(const gss_name_t name, + const char *user); + +extern GSSAPI_LIB_VARIABLE gss_buffer_t GSS_C_ATTR_LOCAL_LOGIN_USER; + /* * Naming extensions */ diff --git a/lib/gssapi/gssapi_mech.h b/lib/gssapi/gssapi_mech.h index 28e2836ee..0f9ab623d 100644 --- a/lib/gssapi/gssapi_mech.h +++ b/lib/gssapi/gssapi_mech.h @@ -460,13 +460,56 @@ struct gss_mo_desc_struct { int (*set)(gss_const_OID, gss_mo_desc *, int, gss_buffer_t); }; +typedef OM_uint32 GSSAPI_CALLCONV _gss_acquire_cred_with_password_t + (OM_uint32 *, /* minor_status */ + const gss_name_t, /* desired_name */ + const gss_buffer_t, /* password */ + OM_uint32, /* time_req */ + const gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 * /* time_rec */ + ); + + +typedef OM_uint32 GSSAPI_CALLCONV _gss_add_cred_with_password_t ( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* input_cred_handle */ + const gss_name_t, /* desired_name */ + const gss_OID, /* desired_mech */ + const gss_buffer_t, /* password */ + gss_cred_usage_t, /* cred_usage */ + OM_uint32, /* initiator_time_req */ + OM_uint32, /* acceptor_time_req */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *, /* initiator_time_rec */ + OM_uint32 * /* acceptor_time_rec */ + ); + +typedef OM_uint32 GSSAPI_CALLCONV _gss_pname_to_uid_t ( + OM_uint32 *, /* minor_status */ + const gss_name_t, /* name */ + const gss_OID, /* mech_type */ + uid_t * /* uidOut */ + ); + +typedef OM_uint32 GSSAPI_CALLCONV _gss_authorize_localname_t ( + OM_uint32 *, /* minor_status */ + const gss_name_t, /* name */ + gss_const_buffer_t, /* user */ + gss_const_OID /* user_name_type */ + ); + +/* mechglue internal */ +struct gss_mech_compat_desc_struct; #define GMI_VERSION 5 /* gm_flags */ #define GM_USE_MG_CRED 1 /* uses mech glue credentials */ - typedef struct gssapi_mech_interface_desc { unsigned gm_version; const char *gm_name; @@ -521,12 +564,17 @@ typedef struct gssapi_mech_interface_desc { _gss_cred_label_set_t *gm_cred_label_set; gss_mo_desc *gm_mo; size_t gm_mo_num; + _gss_pname_to_uid_t *gm_pname_to_uid; + _gss_authorize_localname_t *gm_authorize_localname; _gss_display_name_ext_t *gm_display_name_ext; _gss_inquire_name_t *gm_inquire_name; _gss_get_name_attribute_t *gm_get_name_attribute; _gss_set_name_attribute_t *gm_set_name_attribute; _gss_delete_name_attribute_t *gm_delete_name_attribute; _gss_export_name_composite_t *gm_export_name_composite; + _gss_acquire_cred_with_password_t *gm_acquire_cred_with_password; + _gss_add_cred_with_password_t *gm_add_cred_with_password; + struct gss_mech_compat_desc_struct *gm_compat; } gssapi_mech_interface_desc, *gssapi_mech_interface; gssapi_mech_interface diff --git a/lib/gssapi/krb5/authorize_localname.c b/lib/gssapi/krb5/authorize_localname.c new file mode 100644 index 000000000..18d1d9623 --- /dev/null +++ b/lib/gssapi/krb5/authorize_localname.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2011, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "gsskrb5_locl.h" + +OM_uint32 +_gsskrb5_authorize_localname(OM_uint32 *minor_status, + const gss_name_t input_name, + gss_const_buffer_t user_name, + gss_const_OID user_name_type) +{ + krb5_context context; + krb5_principal princ = (krb5_principal)input_name; + char *user; + int user_ok; + + if (!gss_oid_equal(user_name_type, GSS_C_NT_USER_NAME)) + return GSS_S_BAD_NAMETYPE; + + GSSAPI_KRB5_INIT(&context); + + user = malloc(user_name->length + 1); + if (user == NULL) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + + memcpy(user, user_name->value, user_name->length); + user[user_name->length] = '\0'; + + *minor_status = 0; + user_ok = krb5_kuserok(context, princ, user); + + free(user); + + return user_ok ? GSS_S_COMPLETE : GSS_S_UNAUTHORIZED; +} diff --git a/lib/gssapi/krb5/external.c b/lib/gssapi/krb5/external.c index ac5f0a96c..231542828 100644 --- a/lib/gssapi/krb5/external.c +++ b/lib/gssapi/krb5/external.c @@ -324,6 +324,11 @@ static gssapi_mech_interface_desc krb5_mech = { NULL, krb5_mo, sizeof(krb5_mo) / sizeof(krb5_mo[0]), + _gsskrb5_pname_to_uid, + _gsskrb5_authorize_localname, + NULL, + NULL, + NULL, NULL, NULL, NULL, diff --git a/lib/gssapi/krb5/pname_to_uid.c b/lib/gssapi/krb5/pname_to_uid.c new file mode 100644 index 000000000..c658161fc --- /dev/null +++ b/lib/gssapi/krb5/pname_to_uid.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2011, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "gsskrb5_locl.h" + +OM_uint32 +_gsskrb5_pname_to_uid(OM_uint32 *minor_status, + const gss_name_t pname, + const gss_OID mech_type, + uid_t *uidp) +{ + krb5_error_code ret; + krb5_context context; + krb5_const_principal princ = (krb5_const_principal)pname; + char localname[256]; +#ifdef POSIX_GETPWNAM_R + char pwbuf[2048]; + struct passwd pw, *pwd; +#else + struct passwd *pwd; +#endif + + GSSAPI_KRB5_INIT(&context); + + *minor_status = 0; + + ret = krb5_aname_to_localname(context, princ, + sizeof(localname), localname); + if (ret != 0) { + *minor_status = ret; + return GSS_S_FAILURE; + } + +#ifdef POSIX_GETPWNAM_R + if (getpwnam_r(localname, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) { + *minor_status = KRB5_NO_LOCALNAME; + return GSS_S_FAILURE; + } +#else + pwd = getpwnam(localname); +#endif + + if (pwd == NULL) { + *minor_status = KRB5_NO_LOCALNAME; + return GSS_S_FAILURE; + } + + *uidp = pwd->pw_uid; + + return GSS_S_COMPLETE; +} diff --git a/lib/gssapi/libgssapi-exports.def b/lib/gssapi/libgssapi-exports.def index ee3aaf3b3..21989e6db 100644 --- a/lib/gssapi/libgssapi-exports.def +++ b/lib/gssapi/libgssapi-exports.def @@ -11,8 +11,10 @@ EXPORTS __gss_c_attr_stream_sizes_oid_desc DATA gss_accept_sec_context gss_acquire_cred + gss_acquire_cred_with_password gss_add_buffer_set_member gss_add_cred + gss_add_cred_with_password gss_add_oid_set_member gss_canonicalize_name gss_compare_name @@ -21,17 +23,21 @@ EXPORTS gss_create_empty_buffer_set gss_create_empty_oid_set gss_decapsulate_token + gss_delete_name_attribute gss_delete_sec_context gss_display_mech_attr gss_display_name + gss_display_name_ext gss_display_status gss_duplicate_name gss_duplicate_oid gss_encapsulate_token gss_export_cred gss_export_name + gss_export_name_composite gss_export_sec_context gss_get_mic + gss_get_name_attribute gss_import_cred gss_import_name gss_import_sec_context @@ -45,6 +51,7 @@ EXPORTS gss_inquire_cred_by_oid gss_inquire_mech_for_saslname gss_inquire_mechs_for_name + gss_inquire_name gss_inquire_names_for_mech gss_inquire_saslname_for_mech gss_inquire_sec_context_by_oid ;! @@ -64,6 +71,7 @@ EXPORTS gss_oid_to_name gss_oid_equal gss_oid_to_str + gss_pname_to_uid gss_process_context_token gss_pseudo_random gss_release_buffer @@ -75,12 +83,15 @@ EXPORTS gss_release_oid_set gss_seal gss_set_cred_option + gss_set_name_attribute gss_set_sec_context_option gss_sign + gss_store_cred gss_test_oid_set_member gss_unseal gss_unwrap gss_unwrap_iov + gss_userok gss_verify gss_verify_mic gss_wrap diff --git a/lib/gssapi/mech/compat.h b/lib/gssapi/mech/compat.h new file mode 100644 index 000000000..b3ac2f544 --- /dev/null +++ b/lib/gssapi/mech/compat.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2010, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +typedef OM_uint32 GSSAPI_CALLCONV _gss_inquire_saslname_for_mech_t ( + OM_uint32 *, /* minor_status */ + const gss_OID, /* desired_mech */ + gss_buffer_t, /* sasl_mech_name */ + gss_buffer_t, /* mech_name */ + gss_buffer_t /* mech_description */ + ); + +typedef OM_uint32 GSSAPI_CALLCONV _gss_inquire_mech_for_saslname_t ( + OM_uint32 *, /* minor_status */ + const gss_buffer_t, /* sasl_mech_name */ + gss_OID * /* mech_type */ + ); + +typedef OM_uint32 GSSAPI_CALLCONV _gss_inquire_attrs_for_mech_t ( + OM_uint32 *, /* minor_status */ + gss_const_OID, /* mech */ + gss_OID_set *, /* mech_attrs */ + gss_OID_set * /* known_mech_attrs */ + ); + +/* + * API-as-SPI compatibility for compatibility with MIT mechanisms; + * native Heimdal mechanisms should not use these. + */ +struct gss_mech_compat_desc_struct { + _gss_inquire_saslname_for_mech_t *gmc_inquire_saslname_for_mech; + _gss_inquire_mech_for_saslname_t *gmc_inquire_mech_for_saslname; + _gss_inquire_attrs_for_mech_t *gmc_inquire_attrs_for_mech; +}; + diff --git a/lib/gssapi/mech/cred.h b/lib/gssapi/mech/cred.h index adffe6893..5d6430f69 100644 --- a/lib/gssapi/mech/cred.h +++ b/lib/gssapi/mech/cred.h @@ -39,3 +39,6 @@ struct _gss_cred { struct _gss_mechanism_cred_list gc_mc; }; +struct _gss_mechanism_cred * +_gss_copy_cred(struct _gss_mechanism_cred *mc); + diff --git a/lib/gssapi/mech/gss_add_cred_with_password.c b/lib/gssapi/mech/gss_add_cred_with_password.c new file mode 100644 index 000000000..9f205465c --- /dev/null +++ b/lib/gssapi/mech/gss_add_cred_with_password.c @@ -0,0 +1,151 @@ +/*- + * Copyright (c) 2005 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/libgssapi/gss_add_cred.c,v 1.1 2005/12/29 14:40:20 dfr Exp $ + */ + +#include "mech_locl.h" + +GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL +gss_add_cred_with_password(OM_uint32 *minor_status, + const gss_cred_id_t input_cred_handle, + const gss_name_t desired_name, + const gss_OID desired_mech, + const gss_buffer_t password, + gss_cred_usage_t cred_usage, + OM_uint32 initiator_time_req, + OM_uint32 acceptor_time_req, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *initiator_time_rec, + OM_uint32 *acceptor_time_rec) +{ + OM_uint32 major_status; + gssapi_mech_interface m; + struct _gss_cred *cred = (struct _gss_cred *) input_cred_handle; + struct _gss_cred *new_cred; + gss_cred_id_t release_cred; + struct _gss_mechanism_cred *mc, *target_mc, *copy_mc; + struct _gss_mechanism_name *mn; + OM_uint32 junk; + + *minor_status = 0; + *output_cred_handle = GSS_C_NO_CREDENTIAL; + if (initiator_time_rec) + *initiator_time_rec = 0; + if (acceptor_time_rec) + *acceptor_time_rec = 0; + if (actual_mechs) + *actual_mechs = GSS_C_NO_OID_SET; + + new_cred = malloc(sizeof(struct _gss_cred)); + if (!new_cred) { + *minor_status = ENOMEM; + return (GSS_S_FAILURE); + } + HEIM_SLIST_INIT(&new_cred->gc_mc); + + /* + * We go through all the mc attached to the input_cred_handle + * and check the mechanism. If it matches, we call + * gss_add_cred for that mechanism, otherwise we copy the mc + * to new_cred. + */ + target_mc = 0; + if (cred) { + HEIM_SLIST_FOREACH(mc, &cred->gc_mc, gmc_link) { + if (gss_oid_equal(mc->gmc_mech_oid, desired_mech)) { + target_mc = mc; + } + copy_mc = _gss_copy_cred(mc); + if (!copy_mc) { + release_cred = (gss_cred_id_t)new_cred; + gss_release_cred(&junk, &release_cred); + *minor_status = ENOMEM; + return (GSS_S_FAILURE); + } + HEIM_SLIST_INSERT_HEAD(&new_cred->gc_mc, copy_mc, gmc_link); + } + } + + /* + * Figure out a suitable mn, if any. + */ + if (desired_name) { + major_status = _gss_find_mn(minor_status, + (struct _gss_name *) desired_name, + desired_mech, + &mn); + if (major_status != GSS_S_COMPLETE) { + free(new_cred); + return major_status; + } + } else { + mn = 0; + } + + m = __gss_get_mechanism(desired_mech); + if (m->gm_add_cred_with_password == NULL) { + release_cred = (gss_cred_id_t)new_cred; + gss_release_cred(&junk, &release_cred); + return (GSS_S_UNAVAILABLE); + } + + mc = malloc(sizeof(struct _gss_mechanism_cred)); + if (!mc) { + release_cred = (gss_cred_id_t)new_cred; + gss_release_cred(&junk, &release_cred); + *minor_status = ENOMEM; + return (GSS_S_FAILURE); + } + mc->gmc_mech = m; + mc->gmc_mech_oid = &m->gm_mech_oid; + + major_status = m->gm_add_cred_with_password(minor_status, + target_mc ? target_mc->gmc_cred : GSS_C_NO_CREDENTIAL, + desired_name ? mn->gmn_name : GSS_C_NO_NAME, + desired_mech, + password, + cred_usage, + initiator_time_req, + acceptor_time_req, + &mc->gmc_cred, + actual_mechs, + initiator_time_rec, + acceptor_time_rec); + + if (major_status) { + _gss_mg_error(m, major_status, *minor_status); + release_cred = (gss_cred_id_t)new_cred; + gss_release_cred(&junk, &release_cred); + free(mc); + return (major_status); + } + HEIM_SLIST_INSERT_HEAD(&new_cred->gc_mc, mc, gmc_link); + *output_cred_handle = (gss_cred_id_t) new_cred; + + return (GSS_S_COMPLETE); +} + diff --git a/lib/gssapi/mech/gss_authorize_localname.c b/lib/gssapi/mech/gss_authorize_localname.c new file mode 100644 index 000000000..f8a9848b5 --- /dev/null +++ b/lib/gssapi/mech/gss_authorize_localname.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2011, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "mech_locl.h" + +static gss_buffer_desc localLoginUserAttr = { + sizeof("local-login-user"), + "local-login-user" +}; + +gss_buffer_t GSSAPI_LIB_VARIABLE GSS_C_ATTR_LOCAL_LOGIN_USER = &localLoginUserAttr; + +static OM_uint32 +mech_authorize_localname(OM_uint32 *minor_status, + const struct _gss_name *name, + const struct _gss_name *user) +{ + OM_uint32 major_status = GSS_S_NAME_NOT_MN; + struct _gss_mechanism_name *mn; + + HEIM_SLIST_FOREACH(mn, &name->gn_mn, gmn_link) { + gssapi_mech_interface m = mn->gmn_mech; + + if (m->gm_authorize_localname == NULL) { + major_status = GSS_S_UNAVAILABLE; + continue; + } + + major_status = m->gm_authorize_localname(minor_status, + mn->gmn_name, + &user->gn_value, + &user->gn_type); + if (major_status != GSS_S_UNAUTHORIZED) + break; + } + + return major_status; +} + +/* + * Naming extensions based local login authorization. + */ +static OM_uint32 +attr_authorize_localname(OM_uint32 *minor_status, + const struct _gss_name *name, + const struct _gss_name *user) +{ + OM_uint32 major_status = GSS_S_UNAVAILABLE; + int more = -1; + + if (!gss_oid_equal(&user->gn_type, GSS_C_NT_USER_NAME)) + return GSS_S_BAD_NAMETYPE; + + while (more != 0 && major_status != GSS_S_COMPLETE) { + OM_uint32 tmpMajor, tmpMinor; + gss_buffer_desc value; + gss_buffer_desc display_value; + int authenticated = 0, complete = 0; + + tmpMajor = gss_get_name_attribute(minor_status, + (gss_name_t)name, + GSS_C_ATTR_LOCAL_LOGIN_USER, + &authenticated, + &complete, + &value, + &display_value, + &more); + if (GSS_ERROR(major_status)) { + major_status = tmpMajor; + break; + } + + /* If attribute is present, return an authoritative error code. */ + if (authenticated && + value.length == user->gn_value.length && + memcmp(value.value, user->gn_value.value, user->gn_value.length) == 0) + major_status = GSS_S_COMPLETE; + else + major_status = GSS_S_UNAUTHORIZED; + + gss_release_buffer(&tmpMinor, &value); + gss_release_buffer(&tmpMinor, &display_value); + } + + return major_status; +} + +OM_uint32 +gss_authorize_localname(OM_uint32 *minor_status, + const gss_name_t gss_name, + const gss_name_t gss_user) + +{ + OM_uint32 major_status; + const struct _gss_name *name = (const struct _gss_name *) gss_name; + const struct _gss_name *user = (const struct _gss_name *) gss_user; + int mechAvailable = 0; + + *minor_status = 0; + + if (gss_name == GSS_C_NO_NAME || gss_user == GSS_C_NO_NAME) + return GSS_S_CALL_INACCESSIBLE_READ; + + /* + * We should check that the user name is not a mechanism name, but + * as Heimdal always calls the mechanism's gss_import_name(), it's + * not possible to make this check. + */ +#if 0 + if (HEIM_SLIST_FIRST(&user->gn_mn) != NULL) + return GSS_S_BAD_NAME; +#endif + + /* If mech returns yes, we return yes */ + major_status = mech_authorize_localname(minor_status, name, user); + if (major_status == GSS_S_COMPLETE) + return GSS_S_COMPLETE; + else if (major_status != GSS_S_UNAVAILABLE) + mechAvailable = 1; + + /* If attribute exists, it is authoritative */ + major_status = attr_authorize_localname(minor_status, name, user); + if (major_status == GSS_S_COMPLETE || major_status == GSS_S_UNAUTHORIZED) + return major_status; + + /* If mechanism did not implement SPI, compare the local name */ + if (mechAvailable == 0) { + int match = 0; + + major_status = gss_compare_name(minor_status, gss_name, + gss_user, &match); + if (major_status == GSS_S_COMPLETE && match == 0) + major_status = GSS_S_UNAUTHORIZED; + } + + return major_status; +} + +int +gss_userok(const gss_name_t name, + const char *user) +{ + OM_uint32 major_status, minor_status; + gss_buffer_desc userBuf; + gss_name_t userName; + + userBuf.value = (void *)user; + userBuf.length = strlen(user); + + major_status = gss_import_name(&minor_status, &userBuf, + GSS_C_NT_USER_NAME, &userName); + if (GSS_ERROR(major_status)) + return 0; + + major_status = gss_authorize_localname(&minor_status, name, userName); + + gss_release_name(&minor_status, &userName); + + return (major_status == GSS_S_COMPLETE); +} diff --git a/lib/gssapi/mech/gss_mech_switch.c b/lib/gssapi/mech/gss_mech_switch.c index bf364ad63..a0a76a183 100644 --- a/lib/gssapi/mech/gss_mech_switch.c +++ b/lib/gssapi/mech/gss_mech_switch.c @@ -310,6 +310,9 @@ _gss_load_mech(void) m->gm_so = so; m->gm_mech.gm_mech_oid = mech_oid; m->gm_mech.gm_flags = 0; + m->gm_mech.gm_compat = calloc(1, sizeof(struct gss_mech_compat_desc_struct)); + if (m->gm_mech.gm_compat == NULL) + goto bad; major_status = gss_add_oid_set_member(&minor_status, &m->gm_mech.gm_mech_oid, &_gss_mech_oids); @@ -369,6 +372,10 @@ _gss_load_mech(void) OPTSYM(set_name_attribute); OPTSYM(delete_name_attribute); OPTSYM(export_name_composite); + OPTSPISYM(acquire_cred_with_password); + OPTSYM(add_cred_with_password); + OPTSYM(pname_to_uid); + OPTSPISYM(authorize_localname); mi = dlsym(so, "gss_mo_init"); if (mi != NULL) { @@ -376,6 +383,11 @@ _gss_load_mech(void) &m->gm_mech.gm_mo, &m->gm_mech.gm_mo_num); if (GSS_ERROR(major_status)) goto bad; + } else { + /* API-as-SPI compatibility */ + COMPATSYM(inquire_saslname_for_mech); + COMPATSYM(inquire_mech_for_saslname); + COMPATSYM(inquire_attrs_for_mech); } /* pick up the oid sets of names */ @@ -392,6 +404,7 @@ _gss_load_mech(void) bad: if (m != NULL) { + free(m->gm_mech.gm_compat); free(m->gm_mech.gm_mech_oid.elements); free(m); } diff --git a/lib/gssapi/mech/gss_mo.c b/lib/gssapi/mech/gss_mo.c index f00afb091..72f95ea43 100644 --- a/lib/gssapi/mech/gss_mo.c +++ b/lib/gssapi/mech/gss_mo.c @@ -4,6 +4,7 @@ * All rights reserved. * * Portions Copyright (c) 2010 Apple Inc. All rights reserved. + * Portions Copyright (c) 2010 PADL Software Pty Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -35,13 +36,14 @@ #include "mech_locl.h" +#include + static int get_option_def(int def, gss_const_OID mech, gss_mo_desc *mo, gss_buffer_t value) { return def; } - int _gss_mo_get_option_1(gss_const_OID mech, gss_mo_desc *mo, gss_buffer_t value) { @@ -60,10 +62,10 @@ _gss_mo_get_ctx_as_string(gss_const_OID mech, gss_mo_desc *mo, gss_buffer_t valu if (value) { value->value = strdup((char *)mo->ctx); if (value->value == NULL) - return 1; + return GSS_S_FAILURE; value->length = strlen((char *)mo->ctx); } - return 0; + return GSS_S_COMPLETE; } GSSAPI_LIB_FUNCTION int GSSAPI_LIB_CALL @@ -79,7 +81,8 @@ gss_mo_set(gss_const_OID mech, gss_const_OID option, for (n = 0; n < m->gm_mo_num; n++) if (gss_oid_equal(option, m->gm_mo[n].option) && m->gm_mo[n].set) return m->gm_mo[n].set(mech, &m->gm_mo[n], enable, value); - return 0; + + return GSS_S_UNAVAILABLE; } GSSAPI_LIB_FUNCTION int GSSAPI_LIB_CALL @@ -91,13 +94,13 @@ gss_mo_get(gss_const_OID mech, gss_const_OID option, gss_buffer_t value) _mg_buffer_zero(value); if ((m = __gss_get_mechanism(mech)) == NULL) - return 0; + return GSS_S_BAD_MECH; for (n = 0; n < m->gm_mo_num; n++) if (gss_oid_equal(option, m->gm_mo[n].option) && m->gm_mo[n].get) return m->gm_mo[n].get(mech, &m->gm_mo[n], value); - return 0; + return GSS_S_UNAVAILABLE; } static void @@ -147,7 +150,8 @@ gss_mo_name(gss_const_OID mech, gss_const_OID option, gss_buffer_t name) for (n = 0; n < m->gm_mo_num; n++) { if (gss_oid_equal(option, m->gm_mo[n].option)) { /* - * If ther is no name, its because its a GSS_C_MA and there is already a table for that. + * If there is no name, its because its a GSS_C_MA and + * there is already a table for that. */ if (m->gm_mo[n].name) { name->value = strdup(m->gm_mo[n].name); @@ -175,14 +179,86 @@ mo_value(const gss_const_OID mech, gss_const_OID option, gss_buffer_t name) if (name == NULL) return GSS_S_COMPLETE; - if (gss_mo_get(mech, option, name) != 0 && name->length == 0) - return GSS_S_FAILURE; + return gss_mo_get(mech, option, name); +} + +/* code derived from draft-ietf-cat-sasl-gssapi-01 */ +static char basis_32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + +static OM_uint32 +make_sasl_name(OM_uint32 *minor, const gss_OID mech, char sasl_name[16]) +{ + EVP_MD_CTX *ctx; + char *p = sasl_name; + u_char hdr[2], hash[20], *h = hash; + + if (mech->length > 127) + return GSS_S_BAD_MECH; + + hdr[0] = 0x06; + hdr[1] = mech->length; + + ctx = EVP_MD_CTX_create(); + EVP_DigestInit_ex(ctx, EVP_sha1(), NULL); + EVP_DigestUpdate(ctx, hdr, 2); + EVP_DigestUpdate(ctx, mech->elements, mech->length); + EVP_DigestFinal(ctx, hash, NULL); + + memcpy(p, "GS2-", 4); + p += 4; + + *p++ = basis_32[(h[0] >> 3)]; + *p++ = basis_32[((h[0] & 7) << 2) | (h[1] >> 6)]; + *p++ = basis_32[(h[1] & 0x3f) >> 1]; + *p++ = basis_32[((h[1] & 1) << 4) | (h[2] >> 4)]; + *p++ = basis_32[((h[2] & 0xf) << 1) | (h[3] >> 7)]; + *p++ = basis_32[(h[3] & 0x7f) >> 2]; + *p++ = basis_32[((h[3] & 3) << 3) | (h[4] >> 5)]; + *p++ = basis_32[(h[4] & 0x1f)]; + *p++ = basis_32[(h[5] >> 3)]; + *p++ = basis_32[((h[5] & 7) << 2) | (h[6] >> 6)]; + *p++ = basis_32[(h[6] & 0x3f) >> 1]; + + *p = '\0'; return GSS_S_COMPLETE; } +/* + * gss_inquire_saslname_for_mech() wrapper that uses MIT SPI + */ +static OM_uint32 +inquire_saslname_for_mech_compat(OM_uint32 *minor, + const gss_OID desired_mech, + gss_buffer_t sasl_mech_name, + gss_buffer_t mech_name, + gss_buffer_t mech_description) +{ + struct gss_mech_compat_desc_struct *gmc; + gssapi_mech_interface m; + OM_uint32 major; + + m = __gss_get_mechanism(desired_mech); + if (m == NULL) + return GSS_S_BAD_MECH; + + gmc = m->gm_compat; + + if (gmc != NULL && gmc->gmc_inquire_saslname_for_mech != NULL) { + major = gmc->gmc_inquire_saslname_for_mech(minor, + desired_mech, + sasl_mech_name, + mech_name, + mech_description); + } else { + major = GSS_S_UNAVAILABLE; + } + + return major; +} + /** - * Returns differnt protocol names and description of the mechanism. + * Returns different protocol names and description of the mechanism. * * @param minor_status minor status code * @param desired_mech mech list query @@ -215,15 +291,41 @@ gss_inquire_saslname_for_mech(OM_uint32 *minor_status, return GSS_S_BAD_MECH; major = mo_value(desired_mech, GSS_C_MA_SASL_MECH_NAME, sasl_mech_name); - if (major) return major; + if (major == GSS_S_COMPLETE) { + /* Native SPI */ + major = mo_value(desired_mech, GSS_C_MA_MECH_NAME, mech_name); + if (GSS_ERROR(major)) + return major; - major = mo_value(desired_mech, GSS_C_MA_MECH_NAME, mech_name); - if (major) return major; + major = mo_value(desired_mech, GSS_C_MA_MECH_DESCRIPTION, mech_description); + if (GSS_ERROR(major)) + return major; + } - major = mo_value(desired_mech, GSS_C_MA_MECH_DESCRIPTION, mech_description); - if (major) return major; + if (GSS_ERROR(major)) { + /* API-as-SPI compatibility */ + major = inquire_saslname_for_mech_compat(minor_status, + desired_mech, + sasl_mech_name, + mech_name, + mech_description); + } - return GSS_S_COMPLETE; + if (GSS_ERROR(major)) { + /* Algorithmically dervied SASL mechanism name */ + char buf[16]; + gss_buffer_desc tmp = { sizeof(buf) - 1, buf }; + + major = make_sasl_name(minor_status, desired_mech, buf); + if (GSS_ERROR(major)) + return major; + + major = _gss_copy_buffer(minor_status, &tmp, sasl_mech_name); + if (GSS_ERROR(major)) + return major; + } + + return major; } /** @@ -243,29 +345,91 @@ gss_inquire_mech_for_saslname(OM_uint32 *minor_status, { struct _gss_mech_switch *m; gss_buffer_desc name; - OM_uint32 major; + OM_uint32 major, junk; + char buf[16]; _gss_load_mech(); *mech_type = NULL; HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) { + struct gss_mech_compat_desc_struct *gmc; - major = mo_value(&m->gm_mech_oid, GSS_C_MA_SASL_MECH_NAME, &name); - if (major) - continue; - if (name.length == sasl_mech_name->length && - memcmp(name.value, sasl_mech_name->value, name.length) == 0) { - gss_release_buffer(&major, &name); - *mech_type = &m->gm_mech_oid; - return 0; + /* Native SPI */ + major = mo_value(&m->gm_mech_oid, GSS_C_MA_SASL_MECH_NAME, &name); + if (major == GSS_S_COMPLETE && + name.length == sasl_mech_name->length && + memcmp(name.value, sasl_mech_name->value, name.length) == 0) { + gss_release_buffer(&junk, &name); + *mech_type = &m->gm_mech_oid; + return GSS_S_COMPLETE; } - gss_release_buffer(&major, &name); + gss_release_buffer(&junk, &name); + + if (GSS_ERROR(major)) { + /* API-as-SPI compatibility */ + gmc = m->gm_mech.gm_compat; + if (gmc && gmc->gmc_inquire_mech_for_saslname) { + major = gmc->gmc_inquire_mech_for_saslname(minor_status, + sasl_mech_name, + mech_type); + if (major == GSS_S_COMPLETE) + return GSS_S_COMPLETE; + } + } + + if (GSS_ERROR(major)) { + /* Algorithmically dervied SASL mechanism name */ + if (sasl_mech_name->length == 16 && + make_sasl_name(minor_status, &m->gm_mech_oid, buf) == GSS_S_COMPLETE && + memcmp(buf, sasl_mech_name->value, 16) == 0) { + *mech_type = &m->gm_mech_oid; + return GSS_S_COMPLETE; + } + } } return GSS_S_BAD_MECH; } +/* + * Test mechanism against indicated attributes using both Heimdal and + * MIT SPIs. + */ +static int +test_mech_attrs(gssapi_mech_interface mi, + gss_const_OID_set mech_attrs, + gss_const_OID_set against_attrs, + int except) +{ + size_t n, m; + int eq; + + if (against_attrs == GSS_C_NO_OID_SET) + return 1; + + for (n = 0; n < against_attrs->count; n++) { + for (m = 0; m < mi->gm_mo_num; m++) { + eq = gss_oid_equal(mi->gm_mo[m].option, + &against_attrs->elements[n]); + if (eq) + break; + } + if (mech_attrs != GSS_C_NO_OID_SET) { + for (m = 0; m < mech_attrs->count; m++) { + eq = gss_oid_equal(&mech_attrs->elements[m], + &against_attrs->elements[n]); + if (eq) + break; + } + } + if (!eq ^ except) + return 0; + } + + return 1; +} + /** * Return set of mechanism that fullfill the criteria * @@ -286,57 +450,49 @@ gss_indicate_mechs_by_attrs(OM_uint32 * minor_status, gss_OID_set *mechs) { struct _gss_mech_switch *ms; + gss_OID_set mech_attrs = GSS_C_NO_OID_SET; + gss_OID_set known_mech_attrs = GSS_C_NO_OID_SET; OM_uint32 major; - size_t n, m; major = gss_create_empty_oid_set(minor_status, mechs); - if (major) + if (GSS_ERROR(major)) return major; _gss_load_mech(); HEIM_SLIST_FOREACH(ms, &_gss_mechs, gm_link) { gssapi_mech_interface mi = &ms->gm_mech; + struct gss_mech_compat_desc_struct *gmc = mi->gm_compat; + OM_uint32 tmp; - if (desired_mech_attrs) { - for (n = 0; n < desired_mech_attrs->count; n++) { - for (m = 0; m < mi->gm_mo_num; m++) - if (gss_oid_equal(mi->gm_mo[m].option, &desired_mech_attrs->elements[n])) - break; - if (m == mi->gm_mo_num) - goto next; - } - } + if (gmc && gmc->gmc_inquire_attrs_for_mech) { + major = gmc->gmc_inquire_attrs_for_mech(minor_status, + &mi->gm_mech_oid, + &mech_attrs, + &known_mech_attrs); + if (GSS_ERROR(major)) + continue; + } - if (except_mech_attrs) { - for (n = 0; n < desired_mech_attrs->count; n++) { - for (m = 0; m < mi->gm_mo_num; m++) { - if (gss_oid_equal(mi->gm_mo[m].option, &desired_mech_attrs->elements[n])) - goto next; - } - } - } + /* + * Test mechanism supports all of desired_mech_attrs; + * none of except_mech_attrs; + * and knows of all critical_mech_attrs. + */ + if (test_mech_attrs(mi, mech_attrs, desired_mech_attrs, 0) && + test_mech_attrs(mi, mech_attrs, except_mech_attrs, 1) && + test_mech_attrs(mi, known_mech_attrs, critical_mech_attrs, 0)) { + major = gss_add_oid_set_member(minor_status, &mi->gm_mech_oid, mechs); + } - if (critical_mech_attrs) { - for (n = 0; n < desired_mech_attrs->count; n++) { - for (m = 0; m < mi->gm_mo_num; m++) { - if (mi->gm_mo[m].flags & GSS_MO_MA_CRITICAL) - continue; - if (gss_oid_equal(mi->gm_mo[m].option, &desired_mech_attrs->elements[n])) - break; - } - if (m == mi->gm_mo_num) - goto next; - } - } + gss_release_oid_set(&tmp, &mech_attrs); + gss_release_oid_set(&tmp, &known_mech_attrs); - - next: - do { } while(0); + if (GSS_ERROR(major)) + break; } - - return GSS_S_FAILURE; + return major; } /** @@ -361,30 +517,45 @@ gss_inquire_attrs_for_mech(OM_uint32 * minor_status, { OM_uint32 major, junk; + if (known_mech_attrs) + *known_mech_attrs = GSS_C_NO_OID_SET; + if (mech_attr && mech) { gssapi_mech_interface m; + struct gss_mech_compat_desc_struct *gmc; if ((m = __gss_get_mechanism(mech)) == NULL) { *minor_status = 0; return GSS_S_BAD_MECH; } - major = gss_create_empty_oid_set(minor_status, mech_attr); - if (major != GSS_S_COMPLETE) - return major; + gmc = m->gm_compat; - add_all_mo(m, mech_attr, GSS_MO_MA); - } + if (gmc && gmc->gmc_inquire_attrs_for_mech) { + major = gmc->gmc_inquire_attrs_for_mech(minor_status, + mech, + mech_attr, + known_mech_attrs); + } else { + major = gss_create_empty_oid_set(minor_status, mech_attr); + if (major == GSS_S_COMPLETE) + add_all_mo(m, mech_attr, GSS_MO_MA); + } + if (GSS_ERROR(major)) + return major; + } if (known_mech_attrs) { struct _gss_mech_switch *m; - major = gss_create_empty_oid_set(minor_status, known_mech_attrs); - if (major) { - if (mech_attr) - gss_release_oid_set(&junk, mech_attr); - return major; - } + if (*known_mech_attrs == GSS_C_NO_OID_SET) { + major = gss_create_empty_oid_set(minor_status, known_mech_attrs); + if (GSS_ERROR(major)) { + if (mech_attr) + gss_release_oid_set(&junk, mech_attr); + return major; + } + } _gss_load_mech(); diff --git a/lib/gssapi/mech/gss_pname_to_uid.c b/lib/gssapi/mech/gss_pname_to_uid.c new file mode 100644 index 000000000..659cf6a82 --- /dev/null +++ b/lib/gssapi/mech/gss_pname_to_uid.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2011, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "mech_locl.h" + +static OM_uint32 +mech_pname_to_uid(OM_uint32 *minor_status, + struct _gss_mechanism_name *mn, + uid_t *uidp) +{ + OM_uint32 major_status = GSS_S_UNAVAILABLE; + + *minor_status = 0; + + if (mn->gmn_mech->gm_pname_to_uid == NULL) + return GSS_S_UNAVAILABLE; + + major_status = mn->gmn_mech->gm_pname_to_uid(minor_status, + mn->gmn_name, + mn->gmn_mech_oid, + uidp); + if (GSS_ERROR(major_status)) + _gss_mg_error(mn->gmn_mech, major_status, *minor_status); + + return major_status; +} + +static OM_uint32 +attr_pname_to_uid(OM_uint32 *minor_status, + struct _gss_mechanism_name *mn, + uid_t *uidp) +{ + OM_uint32 major_status = GSS_S_UNAVAILABLE; + OM_uint32 tmpMinor; + int more = -1; + + *minor_status = 0; + + if (mn->gmn_mech->gm_get_name_attribute == NULL) + return GSS_S_UNAVAILABLE; + + while (more != 0) { + gss_buffer_desc value; + gss_buffer_desc display_value; + int authenticated = 0, complete = 0, code; +#ifdef POSIX_GETPWNAM_R + char pwbuf[2048]; + struct passwd pw, *pwd; +#else + struct passwd *pwd; +#endif + char *localname; + + major_status = mn->gmn_mech->gm_get_name_attribute(minor_status, + mn->gmn_name, + GSS_C_ATTR_LOCAL_LOGIN_USER, + &authenticated, + &complete, + &value, + &display_value, + &more); + if (GSS_ERROR(major_status)) { + _gss_mg_error(mn->gmn_mech, major_status, *minor_status); + break; + } + + localname = malloc(value.length + 1); + if (localname == NULL) { + major_status = GSS_S_FAILURE; + *minor_status = ENOMEM; + break; + } + + memcpy(localname, value.value, value.length); + localname[value.length] = '\0'; + +#ifdef POSIX_GETPWNAM_R + if (getpwnam_r(localname, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) + pwd = NULL; +#else + pwd = getpwnam(localname); +#endif + + free(localname); + gss_release_buffer(&tmpMinor, &value); + gss_release_buffer(&tmpMinor, &display_value); + + if (code == 0 && pwd != NULL) { + *uidp = pwd->pw_uid; + major_status = GSS_S_COMPLETE; + *minor_status = 0; + break; + } else + major_status = GSS_S_UNAVAILABLE; + } + + return major_status; +} + +OM_uint32 +gss_pname_to_uid(OM_uint32 *minor_status, + const gss_name_t pname, + const gss_OID mech_type, + uid_t *uidp) +{ + OM_uint32 major_status = GSS_S_UNAVAILABLE; + struct _gss_name *name = (struct _gss_name *) pname; + struct _gss_mechanism_name *mn = NULL; + + *minor_status = 0; + + if (mech_type != GSS_C_NO_OID) { + major_status = _gss_find_mn(minor_status, name, mech_type, &mn); + if (GSS_ERROR(major_status)) + return major_status; + + major_status = mech_pname_to_uid(minor_status, mn, uidp); + if (major_status != GSS_S_COMPLETE) + major_status = attr_pname_to_uid(minor_status, mn, uidp); + } else { + HEIM_SLIST_FOREACH(mn, &name->gn_mn, gmn_link) { + if (mn->gmn_mech->gm_pname_to_uid == NULL) + continue; + + major_status = mech_pname_to_uid(minor_status, mn, uidp); + if (major_status != GSS_S_COMPLETE) + major_status = attr_pname_to_uid(minor_status, mn, uidp); + if (major_status != GSS_S_UNAVAILABLE) + break; + } + } + + if (major_status != GSS_S_COMPLETE && mn != NULL) + _gss_mg_error(mn->gmn_mech, major_status, *minor_status); + + return major_status; +} diff --git a/lib/gssapi/mech/mech_locl.h b/lib/gssapi/mech/mech_locl.h index cb10c23c3..6c23ac525 100644 --- a/lib/gssapi/mech/mech_locl.h +++ b/lib/gssapi/mech/mech_locl.h @@ -62,6 +62,7 @@ #include "mech_switch.h" #include "name.h" #include "utils.h" +#include "compat.h" #define _mg_buffer_zero(buffer) \ do { \ diff --git a/lib/gssapi/test_context.c b/lib/gssapi/test_context.c index d72b898ab..27104284a 100644 --- a/lib/gssapi/test_context.c +++ b/lib/gssapi/test_context.c @@ -581,7 +581,7 @@ main(int argc, char **argv) credential_type, &credential_data, 0, GSS_C_NO_OID, GSS_C_INITIATE, &client_cred); if (GSS_ERROR(maj_stat)) - errx(1, "gss_acquire_cred_ex: %s", + errx(1, "gss_acquire_cred_ext: %s", gssapi_err(maj_stat, min_stat, GSS_C_NO_OID)); gss_release_name(&min_stat, &cname); } else if (credential_type) { diff --git a/lib/gssapi/version-script.map b/lib/gssapi/version-script.map index 82365131b..11e9e3340 100644 --- a/lib/gssapi/version-script.map +++ b/lib/gssapi/version-script.map @@ -2,7 +2,8 @@ HEIMDAL_GSS_2.0 { global: - __gss_c_nt_anonymous; +# __gss_c_nt_anonymous; + __gss_c_nt_anonymous_oid_desc; __gss_c_nt_export_name_oid_desc; __gss_c_nt_hostbased_service_oid_desc; __gss_c_nt_hostbased_service_x_oid_desc; @@ -13,13 +14,15 @@ HEIMDAL_GSS_2.0 { __gss_c_attr_stream_sizes_oid_desc; __gss_c_cred_password_oid_desc; __gss_c_cred_certificate_oid_desc; - gss_accept_sec_context; + GSS_C_ATTR_LOCAL_LOGIN_USER; gss_acquire_cred; gss_acquire_cred_ext; gss_acquire_cred_with_password; gss_add_buffer_set_member; gss_add_cred; + gss_add_cred_with_password; gss_add_oid_set_member; + gss_authorize_localname; gss_canonicalize_name; gss_compare_name; gss_context_query_attributes; @@ -65,6 +68,7 @@ HEIMDAL_GSS_2.0 { gss_mg_collect_error; gss_oid_equal; gss_oid_to_str; + gss_pname_to_uid; gss_process_context_token; gss_pseudo_random; gss_release_buffer; @@ -79,6 +83,7 @@ HEIMDAL_GSS_2.0 { gss_set_name_attribute; gss_set_sec_context_option; gss_sign; + gss_store_cred; gss_test_oid_set_member; gss_unseal; gss_unwrap; diff --git a/lib/krb5/libkrb5-exports.def.in b/lib/krb5/libkrb5-exports.def.in index a61300f70..22c16dfed 100644 --- a/lib/krb5/libkrb5-exports.def.in +++ b/lib/krb5/libkrb5-exports.def.in @@ -396,6 +396,8 @@ EXPORTS krb5_initlog krb5_is_config_principal krb5_is_thread_safe + krb5_kcm_call + krb5_kcm_storage_request krb5_kerberos_enctypes krb5_keyblock_get_enctype krb5_keyblock_init