diff options
Diffstat (limited to 'src/krb5-auth-dialog.c')
-rw-r--r-- | src/krb5-auth-dialog.c | 395 |
1 files changed, 267 insertions, 128 deletions
diff --git a/src/krb5-auth-dialog.c b/src/krb5-auth-dialog.c index f376ffd..c8fc27c 100644 --- a/src/krb5-auth-dialog.c +++ b/src/krb5-auth-dialog.c @@ -43,16 +43,21 @@ #include <libnm_glib.h> #endif +#ifdef HAVE_HX509_ERR_H +# include <hx509_err.h> +#endif + static krb5_context kcontext; static krb5_principal kprincipal; static krb5_timestamp creds_expiry; static krb5_timestamp canceled_creds_expiry; static gboolean canceled; -static gboolean invalid_password; +static gboolean invalid_auth; static gboolean always_run; -static int grab_credentials (Krb5AuthApplet* applet, gboolean renewable); -static gboolean get_tgt_from_ccache (krb5_context context, krb5_creds *creds); +static int grab_credentials (Krb5AuthApplet* applet); +static int ka_renew_credentials (Krb5AuthApplet* applet); +static gboolean ka_get_tgt_from_ccache (krb5_context context, krb5_creds *creds); /* YAY for different Kerberos implementations */ static int @@ -124,18 +129,45 @@ get_principal_realm_data(krb5_principal p) return p->realm.data; #endif } + +static const char* +get_error_message(krb5_context context, krb5_error_code err) +{ + const char *msg = NULL; + +#if defined(HAVE_KRB5_GET_ERROR_MESSAGE) + msg = krb5_get_error_message(context, err); +#else + msg = error_message(err); +#endif + if (msg == NULL) + return "unknown error"; + else + return msg; +} + +static void +ka_krb5_cc_clear_mcred(krb5_creds* mcred) +{ +#if defined HAVE_KRB5_CC_CLEAR_MCRED + krb5_cc_clear_mcred(mcred); +#else + memset(mcred, 0, sizeof(krb5_creds)); +#endif +} + /* ***************************************************************** */ /* ***************************************************************** */ static gboolean -credentials_expiring_real (Krb5AuthApplet* applet, gboolean *renewable) +credentials_expiring_real (Krb5AuthApplet* applet) { krb5_creds my_creds; krb5_timestamp now; gboolean retval = FALSE; - *renewable = FALSE; + applet->renewable = FALSE; - if (!get_tgt_from_ccache (kcontext, &my_creds)) { + if (!ka_get_tgt_from_ccache (kcontext, &my_creds)) { creds_expiry = 0; retval = TRUE; goto out; @@ -152,7 +184,7 @@ credentials_expiring_real (Krb5AuthApplet* applet, gboolean *renewable) /* If our creds are expiring, determine whether they are renewable */ if (retval && get_cred_renewable(&my_creds) && my_creds.times.renew_till > now) { - *renewable = TRUE; + applet->renewable = TRUE; } krb5_free_cred_contents (kcontext, &my_creds); @@ -215,13 +247,12 @@ static gboolean krb5_auth_dialog_do_updates (gpointer data) { Krb5AuthApplet* applet = (Krb5AuthApplet*)data; - gboolean refreshable; g_return_val_if_fail (applet != NULL, FALSE); /* Update creds_expiry and close the applet if we got the creds by other means (e.g. kinit) */ - if (!credentials_expiring_real(applet, &refreshable)) { - KA_DEBUG("PW Dialog persist is %d", applet->pw_dialog_persist); + if (!credentials_expiring_real(applet)) { + KA_DEBUG("PW Dialog persist: %d", applet->pw_dialog_persist); if (!applet->pw_dialog_persist) gtk_widget_hide(applet->pw_dialog); } @@ -275,7 +306,7 @@ krb5_auth_dialog_setup (Krb5AuthApplet *applet, wrong_text = NULL; if (applet->pw_wrong_label) { - if (invalid_password) { + if (invalid_auth) { wrong_text = g_strdup (_("The password you entered is invalid")); } else { krb5_timestamp now; @@ -313,7 +344,7 @@ auth_dialog_prompter (krb5_context ctx, krb5_error_code errcode; int i; - errcode = KRB5_LIBOS_CANTREADPWD; + errcode = KRB5KRB_ERR_GENERIC; canceled = FALSE; canceled_creds_expiry = 0; @@ -321,7 +352,7 @@ auth_dialog_prompter (krb5_context ctx, const gchar *password = NULL; int password_len = 0; int response; - guint32 source_id = 0; + guint32 source_id; GtkWidget *entry; @@ -338,7 +369,6 @@ auth_dialog_prompter (krb5_context ctx, case GTK_RESPONSE_OK: password = gtk_secure_entry_get_text (GTK_SECURE_ENTRY (entry)); password_len = strlen (password); - errcode = 0; break; case GTK_RESPONSE_CANCEL: canceled = TRUE; @@ -350,16 +380,23 @@ auth_dialog_prompter (krb5_context ctx, g_warning ("Unknown Response: %d", response); g_assert_not_reached (); } - g_source_remove (source_id); - prompts[i].reply->data = (char *) password; + if (!password) + goto cleanup; + if (password_len+1 > prompts[i].reply->length) { + g_warning("Password too long %d/%d", password_len+1, prompts[i].reply->length); + goto cleanup; + } + + memcpy(prompts[i].reply->data, (char *) password, password_len + 1); prompts[i].reply->length = password_len; + errcode = 0; } - +cleanup: /* Reset this, so we know the next time we get a TRUE value, it is accurate. */ gtk_widget_hide (applet->pw_dialog); - invalid_password = FALSE; + invalid_auth = FALSE; return errcode; } @@ -394,72 +431,132 @@ network_state_cb (libnm_glib_ctx *context, } #endif - static gboolean credentials_expiring (gpointer *data) { int retval; gboolean give_up; - gboolean renewable; Krb5AuthApplet* applet = (Krb5AuthApplet*) data; - KA_DEBUG("Checking expiry: %d", applet->pw_prompt_secs); - if (credentials_expiring_real (applet, &renewable) && is_online && !applet->show_trayicon) { + KA_DEBUG("Checking expiry <%ds", applet->pw_prompt_secs); + if (credentials_expiring_real (applet) && is_online) { + KA_DEBUG("Expiry @ %ld", creds_expiry); + + if (!ka_renew_credentials (applet)) { + KA_DEBUG("Credentials renewed"); + goto out; + } + + /* no popup when using a trayicon */ + if (applet->show_trayicon) + goto out; + give_up = canceled && (creds_expiry == canceled_creds_expiry); if (!give_up) { do { - retval = grab_credentials (applet, renewable); + retval = grab_credentials (applet); give_up = canceled && (creds_expiry == canceled_creds_expiry); } while ((retval != 0) && (retval != KRB5_REALM_CANT_RESOLVE) && (retval != KRB5_KDC_UNREACH) && - invalid_password && + invalid_auth && !give_up); } } +out: ka_update_status(applet, creds_expiry); return TRUE; } static void -set_options_using_creds(const Krb5AuthApplet* applet, - krb5_context context, - krb5_creds *creds, - krb5_get_init_creds_opt *opts) +set_options_from_creds(const Krb5AuthApplet* applet, + krb5_context context, + krb5_creds *in, + krb5_get_init_creds_opt *out) { krb5_deltat renew_lifetime; int flag; - flag = get_cred_forwardable(creds) != 0; - krb5_get_init_creds_opt_set_forwardable(opts, flag); - flag = get_cred_proxiable(creds) != 0; - krb5_get_init_creds_opt_set_proxiable(opts, flag); - flag = get_cred_renewable(creds) != 0; - if (flag && (creds->times.renew_till > creds->times.starttime)) { - renew_lifetime = creds->times.renew_till - - creds->times.starttime; - krb5_get_init_creds_opt_set_renew_life(opts, +#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_DEFAULT_FLAGS + krb5_get_init_creds_opt_set_default_flags(kcontext, PACKAGE, + krb5_principal_get_realm(kcontext, kprincipal), out); +#endif + + flag = get_cred_forwardable(in) != 0; + krb5_get_init_creds_opt_set_forwardable(out, flag); + flag = get_cred_proxiable(in) != 0; + krb5_get_init_creds_opt_set_proxiable(out, flag); + flag = get_cred_renewable(in) != 0; + if (flag && (in->times.renew_till > in->times.starttime)) { + renew_lifetime = in->times.renew_till - + in->times.starttime; + krb5_get_init_creds_opt_set_renew_life(out, renew_lifetime); } - if (creds->times.endtime > - creds->times.starttime + applet->pw_prompt_secs) { - krb5_get_init_creds_opt_set_tkt_life(opts, - creds->times.endtime - - creds->times.starttime); + if (in->times.endtime > + in->times.starttime + applet->pw_prompt_secs) { + krb5_get_init_creds_opt_set_tkt_life(out, + in->times.endtime - + in->times.starttime); } /* This doesn't do a deep copy -- fix it later. */ - /* krb5_get_init_creds_opt_set_address_list(opts, creds->addresses); */ + /* krb5_get_init_creds_opt_set_address_list(out, creds->addresses); */ +} + + +static krb5_error_code +ka_auth_pkinit(Krb5AuthApplet* applet, krb5_creds* creds) +{ +#ifdef ENABLE_PKINIT + krb5_get_init_creds_opt *opts = NULL; + krb5_error_code retval; + + KA_DEBUG("pkinit with %s", applet->pk_userid); + + if (!applet->pk_userid) + return 0; + + retval = krb5_get_init_creds_opt_alloc (kcontext, &opts); + if (retval) + goto out; + set_options_from_creds (applet, kcontext, creds, opts); + + retval = krb5_get_init_creds_opt_set_pkinit(kcontext, opts, + kprincipal, + applet->pk_userid, + NULL, /* x509 anchors */ + NULL, + NULL, + 0, /* pk_use_enc_key */ + auth_dialog_prompter, + applet, /* data */ + NULL); /* passwd */ + KA_DEBUG("pkinit returned with %d", retval); + if (retval) + goto out; + + retval = krb5_get_init_creds_password(kcontext, creds, kprincipal, + NULL, auth_dialog_prompter, applet, + 0, NULL, opts); +out: + krb5_get_init_creds_opt_free(kcontext, opts); + return retval; +#else /* ENABLE_PKINIT */ + return 0; +#endif /* ! ENABLE_PKINIT */ } + +/* grab credentials interactively */ static int -grab_credentials (Krb5AuthApplet* applet, gboolean renewable) +grab_credentials (Krb5AuthApplet* applet) { krb5_error_code retval; krb5_creds my_creds; krb5_ccache ccache; - krb5_get_init_creds_opt opts; + krb5_get_init_creds_opt *opt = NULL; memset(&my_creds, 0, sizeof(my_creds)); @@ -475,103 +572,148 @@ grab_credentials (Krb5AuthApplet* applet, gboolean renewable) if (retval) return retval; - krb5_get_init_creds_opt_init (&opts); - if (get_tgt_from_ccache (kcontext, &my_creds)) { - set_options_using_creds (applet, kcontext, &my_creds, &opts); - creds_expiry = my_creds.times.endtime; - - if (renewable) { - retval = get_renewed_creds (kcontext, &my_creds, kprincipal, ccache, NULL); - - /* If we succeeded in renewing the credentials, we store it. */ - if (retval == 0) { - goto store; - } - /* Else, try to get new credentials, so just fall through */ - } - krb5_free_cred_contents (kcontext, &my_creds); +#if ENABLE_PKINIT + if (applet->pk_userid) { /* try pkinit */ +#else + if (0) { +#endif + retval = ka_auth_pkinit(applet, &my_creds); } else { - creds_expiry = 0; + retval = krb5_get_init_creds_opt_alloc (kcontext, &opt); + if (retval) + goto out; + set_options_from_creds (applet, kcontext, &my_creds, opt); + retval = krb5_get_init_creds_password(kcontext, &my_creds, kprincipal, + NULL, auth_dialog_prompter, applet, + 0, NULL, opt); } - - retval = krb5_get_init_creds_password(kcontext, &my_creds, kprincipal, - NULL, auth_dialog_prompter, applet, - 0, NULL, &opts); - if (canceled) { + creds_expiry = my_creds.times.endtime; + if (canceled) canceled_creds_expiry = creds_expiry; - } if (retval) { switch (retval) { case KRB5KDC_ERR_PREAUTH_FAILED: case KRB5KRB_AP_ERR_BAD_INTEGRITY: - /* Invalid password, try again. */ - invalid_password = TRUE; +#ifdef HAVE_HX509_ERR_H + case HX509_PKCS11_LOGIN: +#endif + /* Invalid password/pin, try again. */ + invalid_auth = TRUE; goto out; default: + KA_DEBUG("Auth failed with %d: %s", retval, + get_error_message(kcontext, retval)); break; } goto out; } - -store: retval = krb5_cc_initialize(kcontext, ccache, kprincipal); - if (retval) { + if (retval) goto out; - } retval = krb5_cc_store_cred(kcontext, ccache, &my_creds); - if (retval) { + if (retval) goto out; - } - creds_expiry = my_creds.times.endtime; out: + if (opt) + krb5_get_init_creds_opt_free(kcontext, opt); krb5_free_cred_contents (kcontext, &my_creds); krb5_cc_close (kcontext, ccache); return retval; } -static gboolean -get_tgt_from_ccache (krb5_context context, krb5_creds *creds) +/* try to renew the credentials noninteractively */ +static int +ka_renew_credentials (Krb5AuthApplet* applet) { + krb5_error_code retval; + krb5_creds my_creds; krb5_ccache ccache; - krb5_creds mcreds; - krb5_principal principal, tgt_principal; - gboolean ret; - - memset(&ccache, 0, sizeof(ccache)); - ret = FALSE; - if (krb5_cc_default(context, &ccache) == 0) { - memset(&principal, 0, sizeof(principal)); - if (krb5_cc_get_principal(context, ccache, &principal) == 0) { - memset(&tgt_principal, 0, sizeof(tgt_principal)); - if (krb5_build_principal_ext(context, &tgt_principal, - get_principal_realm_length(principal), - get_principal_realm_data(principal), - KRB5_TGS_NAME_SIZE, - KRB5_TGS_NAME, - get_principal_realm_length(principal), - get_principal_realm_data(principal), - 0) == 0) { - memset(creds, 0, sizeof(*creds)); - memset(&mcreds, 0, sizeof(mcreds)); - mcreds.client = principal; - mcreds.server = tgt_principal; - if (krb5_cc_retrieve_cred(context, ccache, - 0, - &mcreds, - creds) == 0) { - ret = TRUE; - } else { - memset(creds, 0, sizeof(*creds)); - } - krb5_free_principal(context, tgt_principal); - } - krb5_free_principal(context, principal); + krb5_get_init_creds_opt opts; + + if (kprincipal == NULL) { + retval = krb5_parse_name(kcontext, applet->principal, + &kprincipal); + if (retval) + return retval; + } + + retval = krb5_cc_default (kcontext, &ccache); + if (retval) + return retval; + + retval = ka_get_tgt_from_ccache (kcontext, &my_creds); + if (!retval) { + krb5_cc_close (kcontext, ccache); + return -1; + } + + krb5_get_init_creds_opt_init (&opts); + set_options_from_creds (applet, kcontext, &my_creds, &opts); + + if (applet->renewable) { + retval = get_renewed_creds (kcontext, &my_creds, kprincipal, ccache, NULL); + if (retval) + goto out; + + retval = krb5_cc_initialize(kcontext, ccache, kprincipal); + if(retval) { + g_warning("krb5_cc_initialize: %s", get_error_message(kcontext, retval)); + goto out; + } + retval = krb5_cc_store_cred(kcontext, ccache, &my_creds); + if (retval) { + g_warning("krb5_cc_store_cred: %s", get_error_message(kcontext, retval)); + goto out; } - krb5_cc_close(context, ccache); } +out: + creds_expiry = my_creds.times.endtime; + krb5_free_cred_contents (kcontext, &my_creds); + krb5_cc_close (kcontext, ccache); + return retval; +} + + +/* get principal associated with the default credentials cache - if found store + * it in *creds, return FALSE otwerwise */ +static gboolean +ka_get_tgt_from_ccache (krb5_context context, krb5_creds *creds) +{ + krb5_ccache ccache; + krb5_creds pattern; + krb5_principal principal; + gboolean ret = FALSE; + + ka_krb5_cc_clear_mcred(&pattern); + + if (krb5_cc_default(context, &ccache)) + return FALSE; + + if (krb5_cc_get_principal(context, ccache, &principal)) + goto out; + + if (krb5_build_principal_ext(context, &pattern.server, + get_principal_realm_length(principal), + get_principal_realm_data(principal), + KRB5_TGS_NAME_SIZE, + KRB5_TGS_NAME, + get_principal_realm_length(principal), + get_principal_realm_data(principal), 0)) { + goto out_free_princ; + } + + pattern.client = principal; + if (!krb5_cc_retrieve_cred(context, ccache, 0, &pattern, creds)) + ret = TRUE; + krb5_free_principal(context, pattern.server); + +out_free_princ: + krb5_free_principal(context, principal); +out: + krb5_cc_close(context, ccache); return ret; } @@ -583,11 +725,10 @@ using_krb5() krb5_creds creds; err = krb5_init_context(&kcontext); - if (err) { - return TRUE; - } + if (err) + return FALSE; - have_tgt = get_tgt_from_ccache(kcontext, &creds); + have_tgt = ka_get_tgt_from_ccache(kcontext, &creds); if (have_tgt) { krb5_copy_principal(kcontext, creds.client, &kprincipal); krb5_free_cred_contents (kcontext, &creds); @@ -604,20 +745,19 @@ ka_destroy_cache (GtkMenuItem *menuitem, gpointer data) krb5_ccache ccache; const char* cache; krb5_error_code ret; - gboolean renewable; cache = krb5_cc_default_name(kcontext); ret = krb5_cc_resolve(kcontext, cache, &ccache); ret = krb5_cc_destroy (kcontext, ccache); - credentials_expiring_real(applet, &renewable); + credentials_expiring_real(applet); } static void ka_error_dialog(int err) { - const char* msg = error_message(err); + const char* msg = get_error_message(kcontext, err); GtkWidget *dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, @@ -633,18 +773,18 @@ void ka_grab_credentials (Krb5AuthApplet* applet) { int retval; - gboolean renewable, retry; + gboolean retry; applet->pw_dialog_persist = TRUE; do { retry = TRUE; - retval = grab_credentials (applet, FALSE); + retval = grab_credentials (applet); + if (invalid_auth) + continue; switch (retval) { - case KRB5KRB_AP_ERR_BAD_INTEGRITY: - retry = TRUE; - break; case 0: /* success */ - case KRB5_LIBOS_CANTREADPWD: /* canceled */ + case KRB5_LIBOS_PWDINTR: /* canceled (heimdal) */ + case KRB5_LIBOS_CANTREADPWD: /* canceled (mit) */ retry = FALSE; break; case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: @@ -656,7 +796,7 @@ ka_grab_credentials (Krb5AuthApplet* applet) } while(retry); applet->pw_dialog_persist = FALSE; - credentials_expiring_real(applet, &renewable); + credentials_expiring_real(applet); } @@ -679,7 +819,7 @@ ka_create_gtk_secure_entry (GladeXML *xml, gchar *func_name, gchar *name, } -static void +static void ka_secmem_init () { /* Initialize secure memory. 1 is too small, so the default size @@ -752,7 +892,6 @@ main (int argc, char *argv[]) applet->pw_dialog = glade_xml_get_widget (applet->pw_xml, "krb5_dialog"); g_set_application_name (_("Network Authentication")); - gtk_window_set_default_icon_name (applet->icons[1]); #ifdef ENABLE_NETWORK_MANAGER nm_context = libnm_glib_init (); |