From 6a45cdd4cc1321567d929f41721792c9e3d38401 Mon Sep 17 00:00:00 2001 From: Guido Günther Date: Sat, 18 Oct 2008 18:17:19 +0200 Subject: Imported Upstream version 0.7.jit26357da --- ChangeLog | 15 ++ README | 4 + configure.ac | 22 +++ gtksecentry/gtksecentry.c | 48 ++--- gtksecentry/gtksecentry.h | 2 +- icons/Makefile.am | 1 + icons/krb-expiring-ticket.png | Bin 0 -> 1341 bytes secmem/Makefile.am | 4 +- secmem/secmem.c | 8 +- secmem/util.h | 8 +- src/Makefile.am | 9 + src/krb5-auth-applet.c | 94 +++++++--- src/krb5-auth-applet.h | 6 +- src/krb5-auth-dialog.c | 395 +++++++++++++++++++++++++++------------- src/krb5-auth-dialog.schemas.in | 55 ++++++ src/krb5-auth-gconf.c | 16 ++ src/krb5-auth-notify.c | 19 +- src/krb5-auth-notify.h | 4 - 18 files changed, 506 insertions(+), 204 deletions(-) create mode 100644 icons/krb-expiring-ticket.png create mode 100644 src/krb5-auth-dialog.schemas.in diff --git a/ChangeLog b/ChangeLog index a205318..f35a038 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2008-09-26 Guido Guenther + + * src/krb5-auth-dialog.glade: remove width_request from + krb5_auth_message_label. Now we display the whole principal name + instead of cutting it off at an arbitrary position. + +2008-09-26 Guido Guenther + + * src/krb5-auth-dialog.1.in: add manpage section + +2008-09-26 Guido Guenther + + * src/krb5-auth-dialog.desktop.in: + remove duplicte exec form desktop file and fix comment + 2008-06-09 Colin Walters Bug 534867: should use g_timeout_add_seconds diff --git a/README b/README index 58a81ea..b090c1f 100644 --- a/README +++ b/README @@ -14,6 +14,10 @@ gconftool-2 --type=bool --set /apps/krb5-auth-dialog/show_trayicon false You can set the time of the first password prompt via: gconftool-2 --type=int --set /apps/krb5-auth-dialog/prompt_minutes 30 +You can set the principals pkinit identifier via: +gconftool-2 --type=string --set /apps/krb5-auth-dialog/pk_userid "FILE:/path/to/user.pem,/path/to/user.key" +or if you're using a smartcard: +gconftool-2 --type=string --set /apps/krb5-auth-dialog/pk_userid "PKCS11:/usr/lib/opensc/opensc-pkcs11.so" A note on translations: Kerberos doesn't translate either its prompts or its error messages. diff --git a/configure.ac b/configure.ac index e947e91..78e9e61 100644 --- a/configure.ac +++ b/configure.ac @@ -54,12 +54,16 @@ savedCFLAGS="$CFLAGS" CFLAGS="$KRB5_CFLAGS $CFLAGS" savedLIBS="$LIBS" LIBS="$KRB5_LIBS $LIBS" +AC_CHECK_HEADERS([hx509_err.h]) AC_CHECK_MEMBERS(krb5_creds.ticket_flags,,,[#include ]) AC_CHECK_MEMBERS(krb5_creds.flags.b.forwardable,,,[#include ]) AC_CHECK_MEMBERS(krb5_creds.flags.b.renewable,,,[#include ]) AC_CHECK_MEMBERS(krb5_creds.flags.b.proxiable,,,[#include ]) AC_CHECK_MEMBERS(krb5_creds.flags,,,[#include ]) +AC_CHECK_FUNCS([krb5_get_error_message]) AC_CHECK_FUNCS([krb5_get_renewed_creds]) +AC_CHECK_FUNCS([krb5_get_init_creds_opt_set_default_flags]) +AC_CHECK_FUNCS([krb5_cc_clear_mcred]) AC_MSG_CHECKING(if a krb5_principal->realm is a char*) AC_COMPILE_IFELSE([ $ac_includes_default @@ -88,9 +92,27 @@ main(int argc, char **argv) }],[AC_DEFINE(HAVE_KRB5_PRINCIPAL_REALM_AS_DATA,1,[Define if the realm of a krb5_principal is a krb5_data]) AC_MSG_RESULT(yes)], AC_MSG_RESULT(no)) +dnl pkinit +AC_MSG_CHECKING([whether to enable pkinit support]) +AC_ARG_ENABLE([pkinit], + AS_HELP_STRING([--enable-pkinit],[whether to enable preauth via pkinit support]), + [],[enable_pkinit=autodetect]) +AC_MSG_RESULT([$enable_pkinit]) + +if test "x$enable_pkinit" != "xno"; then + AC_CHECK_FUNCS([krb5_get_init_creds_opt_set_pkinit], + [enable_pkinit=yes],[enable_pkinit=no]) +fi + +if test "x$enable_pkinit" = "xyes"; then + AC_DEFINE([ENABLE_PKINIT],[1],[Define for pkinit support]) +fi +AM_CONDITIONAL([ENABLE_PKINIT],[test "x$enable_pkinit" = "xyes"]) CFLAGS="$savedCFLAGS" LIBS="$savedLIBS" + + dnl NetworkManager AC_MSG_CHECKING([whether to enable NetworkManager support]) AC_ARG_ENABLE([network-manager], diff --git a/gtksecentry/gtksecentry.c b/gtksecentry/gtksecentry.c index e38b9aa..938479c 100644 --- a/gtksecentry/gtksecentry.c +++ b/gtksecentry/gtksecentry.c @@ -22,7 +22,7 @@ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS * file for a list of people on the GTK+ Team. See the ChangeLog * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ /* @@ -2692,14 +2692,14 @@ gtk_secure_entry_new(void) * be clamped to the range 0-65536. * * Creates a new #GtkSecureEntry widget with the given maximum length. - * + * * Note: the existence of this function is inconsistent * with the rest of the GTK+ API. The normal setup would * be to just require the user to make an extra call * to gtk_secure_entry_set_max_length() instead. It is not * expected that this function will be removed, but * it would be better practice not to use it. - * + * * Return value: a new #GtkSecureEntry. **/ GtkWidget * @@ -2772,7 +2772,7 @@ gtk_secure_entry_set_position(GtkSecureEntry * entry, gint position) * gtk_secure_entry_set_invisible_char: * @entry: a #GtkSecureEntry * @ch: a Unicode character - * + * * Sets the character to use in place of the actual text when * gtk_secure_entry_set_visibility() has been called to set text visibility * to %FALSE. i.e. this is the character used in "password mode" to @@ -2780,7 +2780,7 @@ gtk_secure_entry_set_position(GtkSecureEntry * entry, gint position) * invisible char is an asterisk ('*'). If you set the invisible char * to 0, then the user will get no feedback at all; there will be * no text on the screen as they type. - * + * **/ void gtk_secure_entry_set_invisible_char(GtkSecureEntry * entry, gunichar ch) @@ -2803,7 +2803,7 @@ gtk_secure_entry_set_invisible_char(GtkSecureEntry * entry, gunichar ch) * for entries with visisbility set to false. See gtk_secure_entry_set_invisible_char(). * * Return value: the current invisible char, or 0, if the entry does not - * show invisible text at all. + * show invisible text at all. **/ gunichar gtk_secure_entry_get_invisible_char(GtkSecureEntry * entry) @@ -2846,7 +2846,7 @@ gtk_secure_entry_select_region(GtkSecureEntry * entry, * @max: the maximum length of the entry, or 0 for no maximum. * (other than the maximum length of entries.) The value passed in will * be clamped to the range 0-65536. - * + * * Sets the maximum allowed length of the contents of the widget. If * the current contents are longer than the given length, then they * will be truncated to fit. @@ -2896,7 +2896,7 @@ gtk_secure_entry_get_max_length(GtkSecureEntry * entry) * (For experts: if @setting is %TRUE, the entry calls * gtk_window_activate_default() on the window containing the entry, in * the default handler for the "activate" signal.) - * + * **/ void gtk_secure_entry_set_activates_default(GtkSecureEntry * entry, @@ -2914,9 +2914,9 @@ gtk_secure_entry_set_activates_default(GtkSecureEntry * entry, /** * gtk_secure_entry_get_activates_default: * @entry: a #GtkSecureEntry - * + * * Retrieves the value set by gtk_secure_entry_set_activates_default(). - * + * * Return value: %TRUE if the entry will activate the default widget **/ gboolean @@ -2937,7 +2937,7 @@ gtk_secure_entry_get_activates_default(GtkSecureEntry * entry) * request, the size can still be affected by * how you pack the widget into containers. If @n_chars is -1, the * size reverts to the default entry size. - * + * **/ void gtk_secure_entry_set_width_chars(GtkSecureEntry * entry, gint n_chars) @@ -2954,9 +2954,9 @@ gtk_secure_entry_set_width_chars(GtkSecureEntry * entry, gint n_chars) /** * gtk_secure_entry_get_width_chars: * @entry: a #GtkSecureEntry - * + * * Gets the value set by gtk_secure_entry_set_width_chars(). - * + * * Return value: number of chars to request space for, or negative if unset **/ gint @@ -2971,7 +2971,7 @@ gtk_secure_entry_get_width_chars(GtkSecureEntry * entry) * gtk_secure_entry_set_has_frame: * @entry: a #GtkSecureEntry * @setting: new value - * + * * Sets whether the entry has a beveled frame around it. **/ void @@ -2992,9 +2992,9 @@ gtk_secure_entry_set_has_frame(GtkSecureEntry * entry, gboolean setting) /** * gtk_secure_entry_get_has_frame: * @entry: a #GtkSecureEntry - * + * * Gets the value set by gtk_secure_entry_set_has_frame(). - * + * * Return value: whether the entry has a beveled frame **/ gboolean @@ -3009,7 +3009,7 @@ gtk_secure_entry_get_has_frame(GtkSecureEntry * entry) /** * gtk_secure_entry_get_layout: * @entry: a #GtkSecureEntry - * + * * Gets the #PangoLayout used to display the entry. * The layout is useful to e.g. convert text positions to * pixel positions, in combination with gtk_secure_entry_get_layout_offsets(). @@ -3020,7 +3020,7 @@ gtk_secure_entry_get_has_frame(GtkSecureEntry * entry) * gtk_secure_entry_layout_index_to_text_index() and * gtk_secure_entry_text_index_to_layout_index() are needed to convert byte * indices in the layout to byte indices in the entry contents. - * + * * Return value: the #PangoLayout for this entry **/ PangoLayout * @@ -3040,12 +3040,12 @@ gtk_secure_entry_get_layout(GtkSecureEntry * entry) * gtk_secure_entry_layout_index_to_text_index: * @entry: a #GtkSecureEntry * @layout_index: byte index into the entry layout text - * + * * Converts from a position in the entry contents (returned * by gtk_secure_entry_get_text()) to a position in the * entry's #PangoLayout (returned by gtk_secure_entry_get_layout(), * with text retrieved via pango_layout_get_text()). - * + * * Return value: byte index into the entry contents **/ gint @@ -3077,11 +3077,11 @@ gtk_secure_entry_layout_index_to_text_index(GtkSecureEntry * entry, * gtk_secure_entry_text_index_to_layout_index: * @entry: a #GtkSecureEntry * @text_index: byte index into the entry contents - * + * * Converts from a position in the entry's #PangoLayout(returned by * gtk_secure_entry_get_layout()) to a position in the entry contents * (returned by gtk_secure_entry_get_text()). - * + * * Return value: byte index into the entry layout text **/ gint @@ -3119,7 +3119,7 @@ gtk_secure_entry_text_index_to_layout_index(GtkSecureEntry * entry, * Also useful to convert mouse events into coordinates inside the * #PangoLayout, e.g. to take some action if some part of the entry text * is clicked. - * + * * Note that as the user scrolls around in the entry the offsets will * change; you'll need to connect to the "notify::scroll_offset" * signal to track this. Remember when using the #PangoLayout @@ -3130,7 +3130,7 @@ gtk_secure_entry_text_index_to_layout_index(GtkSecureEntry * entry, * gtk_secure_entry_layout_index_to_text_index() and * gtk_secure_entry_text_index_to_layout_index() are needed to convert byte * indices in the layout to byte indices in the entry contents. - * + * **/ void gtk_secure_entry_get_layout_offsets(GtkSecureEntry * entry, diff --git a/gtksecentry/gtksecentry.h b/gtksecentry/gtksecentry.h index f621ee2..12736cd 100644 --- a/gtksecentry/gtksecentry.h +++ b/gtksecentry/gtksecentry.h @@ -22,7 +22,7 @@ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS * file for a list of people on the GTK+ Team. See the ChangeLog * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ /* diff --git a/icons/Makefile.am b/icons/Makefile.am index b771676..d13dbbf 100644 --- a/icons/Makefile.am +++ b/icons/Makefile.am @@ -4,6 +4,7 @@ smallicondir=${datadir}/icons/hicolor/22x22/apps smallicon_DATA= \ krb-valid-ticket.png \ krb-no-valid-ticket.png \ + krb-expiring-ticket.png \ $(NULL) EXTRA_DIST=\ diff --git a/icons/krb-expiring-ticket.png b/icons/krb-expiring-ticket.png new file mode 100644 index 0000000..cd03697 Binary files /dev/null and b/icons/krb-expiring-ticket.png differ diff --git a/secmem/Makefile.am b/secmem/Makefile.am index 331530f..dfdbed0 100644 --- a/secmem/Makefile.am +++ b/secmem/Makefile.am @@ -7,12 +7,12 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -# +# # PINENTRY is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA diff --git a/secmem/secmem.c b/secmem/secmem.c index 796108d..fc9ac2f 100644 --- a/secmem/secmem.c +++ b/secmem/secmem.c @@ -58,21 +58,21 @@ typedef union { #define log_error log_info #define log_bug log_fatal -void +void log_info(char *template, ...) { va_list args; - + va_start(args, template); vfprintf(stderr, template, args); va_end(args); } -void +void log_fatal(char *template, ...) { va_list args; - + va_start(args, template); vfprintf(stderr, template, args); va_end(args); diff --git a/secmem/util.h b/secmem/util.h index 7986c99..5d28925 100644 --- a/secmem/util.h +++ b/secmem/util.h @@ -23,18 +23,18 @@ #include #ifndef HAVE_BYTE_TYPEDEF -# undef byte +# undef byte # ifdef __riscos__ /* Norcroft treats char == unsigned char but char* != unsigned char* */ typedef char byte; -# else +# else typedef unsigned char byte; -# endif +# endif # define HAVE_BYTE_TYPEDEF #endif #ifndef HAVE_ULONG_TYPEDEF -# undef ulong +# undef ulong typedef unsigned long ulong; # define HAVE_ULONG_TYPEDEF #endif diff --git a/src/Makefile.am b/src/Makefile.am index 066e582..2837be8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,6 +10,13 @@ autostart_in_files = krb5-auth-dialog.desktop.in autostart_DATA = $(autostart_in_files:.desktop.in=.desktop) @INTLTOOL_DESKTOP_RULE@ +schemadir = $(sysconfdir)/gconf/schemas +schema_in_files = $(PACKAGE).schemas.in +schema_DATA = $(PACKAGE).schemas + +%.schemas: $(srcdir)/%.schemas.in + sed -e "s,::PACKAGE::,$(PACKAGE)," < $< > $@ + krb5_auth_dialog_SOURCES = \ krb5-auth-dialog.c \ krb5-auth-dialog.h \ @@ -42,7 +49,9 @@ glade_DATA = \ EXTRA_DIST = \ $(glade_DATA) \ + $(schama_in_files) \ $(autostart_in_files) \ krb5-auth-dialog.1.in +CLEANFILES = $(schema_DATA) DISTCLEANFILES = krb5-auth-dialog.desktop diff --git a/src/krb5-auth-applet.c b/src/krb5-auth-applet.c index 4a11d09..c915240 100644 --- a/src/krb5-auth-applet.c +++ b/src/krb5-auth-applet.c @@ -24,65 +24,97 @@ #include "krb5-auth-applet.h" #include "krb5-auth-dialog.h" -#ifdef HAVE_LIBNOTIFY #include "krb5-auth-notify.h" -#endif #define NOTIFY_SECONDS 300 -/* update the tray icon's tooltip and icon */ -int -ka_update_status(Krb5AuthApplet* applet, krb5_timestamp expiry) +enum ka_icon { + inv_icon = 0, + exp_icon, + val_icon, +}; + + +/* determine the new tooltip text */ +static char* +ka_tooltip_text(Krb5AuthApplet* applet, int remaining) { - gchar* expiry_text; - int now = time(0); - int remaining = expiry - now; - static int last_warn = 0; - static gboolean expiry_notified = FALSE; + int hours, minutes; + gchar* tooltip_text; if (remaining > 0) { - int hours, minutes; if (remaining >= 3600) { hours = remaining / 3600; minutes = (remaining % 3600) / 60; - expiry_text = g_strdup_printf (_("Your credentials expire in %.2d:%.2dh"), hours, minutes); + tooltip_text = g_strdup_printf (_("Your credentials expire in %.2d:%.2dh"), hours, minutes); } else { minutes = remaining / 60; - expiry_text = g_strdup_printf (ngettext( + tooltip_text = g_strdup_printf (ngettext( "Your credentials expire in %d minute", "Your credentials expire in %d minutes", minutes), minutes); } - gtk_status_icon_set_from_icon_name (applet->tray_icon, applet->icons[1]); -#ifdef HAVE_LIBNOTIFY + } else + tooltip_text = g_strdup (_("Your credentials have expired")); + return tooltip_text; +} + + +/* determine the current icon */ +static const char* +ka_select_icon(Krb5AuthApplet* applet, int remaining) +{ + enum ka_icon tray_icon = inv_icon; + + if (remaining > 0) { + if (remaining < applet->pw_prompt_secs && + !applet->renewable) + tray_icon = exp_icon; + else + tray_icon = val_icon; + } + + return applet->icons[tray_icon]; +} + + +/* update the tray icon's tooltip and icon */ +int +ka_update_status(Krb5AuthApplet* applet, krb5_timestamp expiry) +{ + int now = time(0); + int remaining = expiry - now; + static int last_warn = 0; + static gboolean expiry_notified = FALSE; + const char* tray_icon = ka_select_icon (applet, remaining); + char* tooltip_text = ka_tooltip_text (applet, remaining); + + if (remaining > 0) { if (expiry_notified) { - ka_send_event_notification (applet, NOTIFY_URGENCY_NORMAL, + ka_send_event_notification (applet, _("Network credentials valid"), _("Your Kerberos credentials have been refreshed."), NULL); expiry_notified = FALSE; - } else if (remaining < applet->pw_prompt_secs && (now - last_warn) > NOTIFY_SECONDS) { - ka_send_event_notification (applet, NOTIFY_URGENCY_NORMAL, + } else if (remaining < applet->pw_prompt_secs && (now - last_warn) > NOTIFY_SECONDS && + !applet->renewable) { + ka_send_event_notification (applet, _("Network credentials expiring"), - expiry_text, NULL); + tooltip_text, NULL); last_warn = now; } -#endif } else { - expiry_text = g_strdup (_("Your credentials have expired")); - gtk_status_icon_set_from_icon_name (applet->tray_icon, applet->icons[0]); -#ifdef HAVE_LIBNOTIFY if (!expiry_notified) { - ka_send_event_notification (applet, NOTIFY_URGENCY_NORMAL, + ka_send_event_notification (applet, _("Network credentials expired"), _("Your Kerberos credentails have expired."), NULL); expiry_notified = TRUE; last_warn = 0; } -#endif } - gtk_status_icon_set_tooltip (applet->tray_icon, expiry_text); - g_free (expiry_text); + gtk_status_icon_set_from_icon_name (applet->tray_icon, tray_icon); + gtk_status_icon_set_tooltip (applet->tray_icon, tooltip_text); + g_free(tooltip_text); return 0; } @@ -213,7 +245,7 @@ ka_create_tray_icon (Krb5AuthApplet* applet) g_signal_connect (G_OBJECT(tray_icon), "popup-menu", G_CALLBACK(ka_tray_icon_on_menu), applet); - gtk_status_icon_set_from_icon_name (tray_icon, applet->icons[0]); + gtk_status_icon_set_from_icon_name (tray_icon, applet->icons[exp_icon]); gtk_status_icon_set_tooltip (tray_icon, PACKAGE); return tray_icon; } @@ -222,8 +254,9 @@ ka_create_tray_icon (Krb5AuthApplet* applet) int ka_setup_icons (Krb5AuthApplet* applet) { - applet->icons[0] = "krb-no-valid-ticket"; - applet->icons[1] = "krb-valid-ticket"; + applet->icons[val_icon] = "krb-valid-ticket"; + applet->icons[exp_icon] = "krb-expiring-ticket"; + applet->icons[inv_icon] = "krb-no-valid-ticket"; return TRUE; } @@ -240,6 +273,7 @@ ka_create_applet() g_error ("Failure to create tray icon"); if (!(applet->context_menu = ka_create_context_menu (applet))) g_error ("Failure to create context menu"); + gtk_window_set_default_icon_name (applet->icons[val_icon]); ka_show_tray_icon (applet); return applet; diff --git a/src/krb5-auth-applet.h b/src/krb5-auth-applet.h index 062e148..e137794 100644 --- a/src/krb5-auth-applet.h +++ b/src/krb5-auth-applet.h @@ -35,7 +35,7 @@ typedef struct { GtkStatusIcon* tray_icon; /* the tray icon */ GtkWidget* context_menu; /* the tray icon's context menu */ - const char* icons[2]; /* for expired and valid tickts */ + const char* icons[3]; /* for invalid, expiring and valid tickts */ gboolean show_trayicon; /* show the trayicon */ /* The password dialog */ @@ -49,6 +49,8 @@ typedef struct { NotifyNotification* notification;/* notification messages */ #endif /* HAVE_LIBNOTIFY */ char* principal; /* the principal to request */ + gboolean renewable; /* credentials renewable? */ + char* pk_userid; /* "userid" for pkint */ } Krb5AuthApplet; Krb5AuthApplet* ka_create_applet(); @@ -59,7 +61,7 @@ gboolean ka_show_tray_icon(Krb5AuthApplet* applet); #ifdef ENABLE_DEBUG #define KA_DEBUG(fmt,...) \ - g_printf ("DEBUG: %s: " fmt "\n", __func__, __VA_ARGS__) + g_printf ("DEBUG: %s: " fmt "\n", __func__, ##__VA_ARGS__) #else #define KA_DEBUG(fmt,...) \ do { } while (0) 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 #endif +#ifdef HAVE_HX509_ERR_H +# include +#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 (); diff --git a/src/krb5-auth-dialog.schemas.in b/src/krb5-auth-dialog.schemas.in new file mode 100644 index 0000000..2a3a707 --- /dev/null +++ b/src/krb5-auth-dialog.schemas.in @@ -0,0 +1,55 @@ + + + + /schemas/apps/::PACKAGE::/show_trayicon + /apps/::PACKAGE::/show_trayicon + ::PACKAGE:: + bool + 1 + + + Show trayicon + Show a trayicon in the status area of the panel + + + + + /schemas/apps/::PACKAGE::/principal + /apps/::PACKAGE::/principal + ::PACKAGE:: + string + + + + Kerberos principal + The kerberos principal to acquire the ticket for + + + + + /schemas/apps/::PACKAGE::/pk_userid + /apps/::PACKAGE::/pk_userid + ::PACKAGE:: + string + + + + Pkinit identifier + The principal's public/private/certificate identifier when using pkinit + + + + + /schemas/apps/::PACKAGE::/prompt_minutes + /apps/::PACKAGE::/prompt_minutes + ::PACKAGE:: + int + 30 + + + Prompt minutes before expiry + Start prompting/displaying notifications that many minutes before expiry + + + + diff --git a/src/krb5-auth-gconf.c b/src/krb5-auth-gconf.c index 20d22ce..a11370c 100644 --- a/src/krb5-auth-gconf.c +++ b/src/krb5-auth-gconf.c @@ -26,6 +26,7 @@ #define KA_GCONF_PATH "/apps/" PACKAGE #define KA_GCONF_KEY_PRINCIPAL KA_GCONF_PATH "/principal" +#define KA_GCONF_KEY_PK_USERID KA_GCONF_PATH "/pk_userid" #define KA_GCONF_KEY_PROMPT_MINS KA_GCONF_PATH "/prompt_minutes" #define KA_GCONF_KEY_SHOW_TRAYICON KA_GCONF_PATH "/show_trayicon" @@ -123,6 +124,18 @@ ka_gconf_set_principal (GConfClient* client, Krb5AuthApplet* applet) } +static gboolean +ka_gconf_set_pk_userid (GConfClient* client, Krb5AuthApplet* applet) +{ + g_free (applet->pk_userid); + if(!ka_gconf_get_string (client, KA_GCONF_KEY_PK_USERID, &applet->pk_userid)) { + applet->pk_userid = NULL; + } + KA_DEBUG("Setting pk_userid to %s", applet->pk_userid ? applet->pk_userid : ""); + return TRUE; +} + + static gboolean ka_gconf_set_prompt_mins (GConfClient* client, Krb5AuthApplet* applet) { @@ -168,6 +181,8 @@ ka_gconf_key_changed_callback (GConfClient* client, ka_gconf_set_prompt_mins (client, applet); } else if (g_strcmp0 (key, KA_GCONF_KEY_SHOW_TRAYICON) == 0) { ka_gconf_set_show_trayicon (client, applet); + } else if (g_strcmp0 (key, KA_GCONF_KEY_PK_USERID) == 0) { + ka_gconf_set_pk_userid (client, applet); } else g_warning("Received notification for unknown gconf key %s", key); return; @@ -195,6 +210,7 @@ ka_gconf_init (Krb5AuthApplet* applet, int argc, char* argv[]) ka_gconf_set_principal (client, applet); ka_gconf_set_prompt_mins (client, applet); ka_gconf_set_show_trayicon (client, applet); + ka_gconf_set_pk_userid(client, applet); success = TRUE; out: diff --git a/src/krb5-auth-notify.c b/src/krb5-auth-notify.c index 3186a47..019662e 100644 --- a/src/krb5-auth-notify.c +++ b/src/krb5-auth-notify.c @@ -18,15 +18,14 @@ */ #include "config.h" - -#ifdef HAVE_LIBNOTIFY - #include "krb5-auth-applet.h" #include "krb5-auth-notify.h" +#ifdef HAVE_LIBNOTIFY +#include + void ka_send_event_notification (Krb5AuthApplet *applet, - NotifyUrgency urgency, const char *summary, const char *message, const char *icon) @@ -50,8 +49,18 @@ ka_send_event_notification (Krb5AuthApplet *applet, applet->notification = \ notify_notification_new_with_status_icon(summary, message, notify_icon, applet->tray_icon); - notify_notification_set_urgency (applet->notification, urgency); + notify_notification_set_urgency (applet->notification, NOTIFY_URGENCY_NORMAL); notify_notification_show (applet->notification, NULL); } +#else /* HAVE_LIBNOTIFY */ + +void +ka_send_event_notification (Krb5AuthApplet *applet __attribute__((__unused__)), + const char *summary __attribute__((__unused__)), + const char *message __attribute__((__unused__)), + const char *icon __attribute__((__unused__))) +{ +} + #endif /* HAVE_LIBNOTIFY */ diff --git a/src/krb5-auth-notify.h b/src/krb5-auth-notify.h index 7d27b8a..9c0ce55 100644 --- a/src/krb5-auth-notify.h +++ b/src/krb5-auth-notify.h @@ -21,12 +21,8 @@ #ifndef KRB5_AUTH_NOTIFY_H #define KRB5_AUTH_NOTIFY_H -#include - void ka_send_event_notification (Krb5AuthApplet *applet, - NotifyUrgency urgency, const char *summary, const char *message, const char *icon); #endif - -- cgit