use GtkSecureEntry instead of GtkEntry for the password entry
[jelmer/krb5-auth-dialog.git] / src / krb5-auth-dialog.c
1 /*
2  * Copyright (C) 2004,2005,2006 Red Hat, Inc.
3  * Authored by Christopher Aillon <caillon@redhat.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  */
20
21 #include "config.h"
22
23 #include <stdlib.h>
24 #include <time.h>
25 #include <krb5.h>
26 #include <stdio.h>
27 #include <sys/wait.h>
28 #include <string.h>
29 #include <gtk/gtk.h>
30 #include <glib/gi18n.h>
31 #include <glade/glade.h>
32
33 #include "gtksecentry.h"
34 #include "secmem-util.h"
35 #include "memory.h"
36
37 #include "krb5-auth-dialog.h"
38 #include "krb5-auth-applet.h"
39 #include "krb5-auth-gconf.h"
40 #include "krb5-auth-dbus.h"
41
42 #ifdef ENABLE_NETWORK_MANAGER
43 #include <libnm_glib.h>
44 #endif
45
46 static krb5_context kcontext;
47 static krb5_principal kprincipal;
48 static krb5_timestamp creds_expiry;
49 static krb5_timestamp canceled_creds_expiry;
50 static gboolean canceled;
51 static gboolean invalid_password;
52 static gboolean always_run;
53
54 static int grab_credentials (Krb5AuthApplet* applet, gboolean renewable);
55 static gboolean get_tgt_from_ccache (krb5_context context, krb5_creds *creds);
56
57 /* YAY for different Kerberos implementations */
58 static int
59 get_cred_forwardable(krb5_creds *creds)
60 {
61 #if defined(HAVE_KRB5_CREDS_TICKET_FLAGS) && defined(TKT_FLG_FORWARDABLE)
62         return creds->ticket_flags & TKT_FLG_FORWARDABLE;
63 #elif defined(HAVE_KRB5_CREDS_FLAGS_B_FORWARDABLE)
64         return creds->flags.b.forwardable;
65 #elif defined(HAVE_KRB5_CREDS_FLAGS) && defined(KDC_OPT_FORWARDABLE)
66         return creds->flags & KDC_OPT_FORWARDABLE;
67 #endif
68 }
69
70 static int
71 get_cred_renewable(krb5_creds *creds)
72 {
73 #if defined(HAVE_KRB5_CREDS_TICKET_FLAGS) && defined(TKT_FLG_RENEWABLE)
74         return creds->ticket_flags & TKT_FLG_RENEWABLE;
75 #elif defined(HAVE_KRB5_CREDS_FLAGS_B_RENEWABLE)
76         return creds->flags.b.renewable;
77 #elif defined(HAVE_KRB5_CREDS_FLAGS) && defined(KDC_OPT_RENEWABLE)
78         return creds->flags & KDC_OPT_RENEWABLE;
79 #endif
80 }
81
82 static krb5_error_code
83 get_renewed_creds(krb5_context context,
84                   krb5_creds *creds,
85                   krb5_principal client,
86                   krb5_ccache ccache,
87                   char *in_tkt_service)
88 {
89 #ifdef HAVE_KRB5_GET_RENEWED_CREDS
90         return krb5_get_renewed_creds (context, creds, client, ccache, in_tkt_service);
91 #else
92         return 1; /* XXX is there something better to return? */
93 #endif
94 }
95
96 static int
97 get_cred_proxiable(krb5_creds *creds)
98 {
99 #if defined(HAVE_KRB5_CREDS_TICKET_FLAGS) && defined(TKT_FLG_PROXIABLE)
100         return creds->ticket_flags & TKT_FLG_PROXIABLE;
101 #elif defined(HAVE_KRB5_CREDS_FLAGS_B_PROXIABLE)
102         return creds->flags.b.proxiable;
103 #elif defined(HAVE_KRB5_CREDS_FLAGS) && defined(KDC_OPT_PROXIABLE)
104         return creds->flags & KDC_OPT_PROXIABLE;
105 #endif
106 }
107
108 static size_t
109 get_principal_realm_length(krb5_principal p)
110 {
111 #if defined(HAVE_KRB5_PRINCIPAL_REALM_AS_STRING)
112         return strlen(p->realm);
113 #elif defined(HAVE_KRB5_PRINCIPAL_REALM_AS_DATA)
114         return p->realm.length;
115 #endif
116 }
117
118 static const char *
119 get_principal_realm_data(krb5_principal p)
120 {
121 #if defined(HAVE_KRB5_PRINCIPAL_REALM_AS_STRING)
122         return p->realm;
123 #elif defined(HAVE_KRB5_PRINCIPAL_REALM_AS_DATA)
124         return p->realm.data;
125 #endif
126 }
127 /* ***************************************************************** */
128 /* ***************************************************************** */
129
130 static gboolean
131 credentials_expiring_real (Krb5AuthApplet* applet, gboolean *renewable)
132 {
133         krb5_creds my_creds;
134         krb5_timestamp now;
135         gboolean retval = FALSE;
136         *renewable = FALSE;
137
138         if (!get_tgt_from_ccache (kcontext, &my_creds)) {
139                 creds_expiry = 0;
140                 retval = TRUE;
141                 goto out;
142         }
143
144         if (krb5_principal_compare (kcontext, my_creds.client, kprincipal)) {
145                 krb5_free_principal(kcontext, kprincipal);
146                 krb5_copy_principal(kcontext, my_creds.client, &kprincipal);
147         }
148         creds_expiry = my_creds.times.endtime;
149         if ((krb5_timeofday(kcontext, &now) == 0) &&
150             (now + applet->pw_prompt_secs > my_creds.times.endtime))
151                 retval = TRUE;
152
153         /* If our creds are expiring, determine whether they are renewable */
154         if (retval && get_cred_renewable(&my_creds) && my_creds.times.renew_till > now) {
155                 *renewable = TRUE;
156         }
157
158         krb5_free_cred_contents (kcontext, &my_creds);
159
160 out:
161         ka_update_status(applet, creds_expiry);
162         return retval;
163 }
164
165
166 static gchar* minutes_to_expiry_text (int minutes)
167 {
168         gchar *expiry_text;
169         gchar *tmp;
170
171         if (minutes > 0) {
172                 expiry_text = g_strdup_printf (ngettext("Your credentials expire in %d minute",
173                                                         "Your credentials expire in %d minutes",
174                                                         minutes),
175                                                minutes);
176         } else {
177                 expiry_text = g_strdup (_("Your credentials have expired"));
178                 tmp = g_strdup_printf ("<span foreground=\"red\">%s</span>", expiry_text);
179                 g_free (expiry_text);
180                 expiry_text = tmp;
181         }
182
183         return expiry_text;
184 }
185
186
187 static gboolean
188 krb5_auth_dialog_wrong_label_update_expiry (GtkWidget* label)
189 {
190         int minutes_left;
191         krb5_timestamp now;
192         gchar *expiry_text;
193         gchar *expiry_markup;
194
195         g_return_val_if_fail (label!= NULL, FALSE);
196
197         if (krb5_timeofday(kcontext, &now) != 0) {
198                 return TRUE;
199         }
200
201         minutes_left = (creds_expiry - now) / 60;
202         expiry_text = minutes_to_expiry_text (minutes_left);
203
204         expiry_markup = g_strdup_printf ("<span size=\"smaller\" style=\"italic\">%s</span>", expiry_text);
205         gtk_label_set_markup (GTK_LABEL (label), expiry_markup);
206         g_free (expiry_text);
207         g_free (expiry_markup);
208
209         return TRUE;
210 }
211
212
213 /* Check for things we have to do while the password dialog is open */
214 static gboolean
215 krb5_auth_dialog_do_updates (gpointer data)
216 {
217         Krb5AuthApplet* applet = (Krb5AuthApplet*)data;
218         gboolean refreshable;
219
220         g_return_val_if_fail (applet != NULL, FALSE);
221
222         /* Update creds_expiry and close the applet if we got the creds by other means (e.g. kinit) */
223         if (!credentials_expiring_real(applet, &refreshable)) {
224                 KA_DEBUG("PW Dialog persist is %d", applet->pw_dialog_persist);
225                 if (!applet->pw_dialog_persist)
226                         gtk_widget_hide(applet->pw_dialog);
227         }
228
229         /* Update the expiry information in the dialog */
230         krb5_auth_dialog_wrong_label_update_expiry (applet->pw_wrong_label);
231         return TRUE;
232 }
233
234
235 static void
236 krb5_auth_dialog_setup (Krb5AuthApplet *applet,
237                         const gchar *krb5prompt,
238                         gboolean hide_password)
239 {
240         GtkWidget *entry;
241         GtkWidget *label;
242         gchar *wrong_text;
243         gchar *wrong_markup;
244         gchar *prompt;
245         int pw4len;
246
247
248         if (krb5prompt == NULL) {
249                 prompt = g_strdup (_("Please enter your Kerberos password."));
250         } else {
251                 /* Kerberos's prompts are a mess, and basically impossible to
252                  * translate.  There's basically no way short of doing a lot of
253                  * string parsing to translate them.  The most common prompt is
254                  * "Password for $uid:".  We special case that one at least.  We
255                  * cannot do any of the fancier strings (like challenges),
256                  * though. */
257                 pw4len = strlen ("Password for ");
258                 if (strncmp (krb5prompt, "Password for ", pw4len) == 0) {
259                         gchar *uid = (gchar *) (krb5prompt + pw4len);
260                         prompt = g_strdup_printf (_("Please enter the password for '%s'"), uid);
261                 } else {
262                         prompt = g_strdup (krb5prompt);
263                 }
264         }
265
266         /* Clear the password entry field */
267         entry = glade_xml_get_widget (applet->pw_xml, "krb5_entry");
268         gtk_secure_entry_set_text (GTK_SECURE_ENTRY (entry), "");
269
270         /* Use the prompt label that krb5 provides us */
271         label = glade_xml_get_widget (applet->pw_xml, "krb5_message_label");
272         gtk_label_set_text (GTK_LABEL (label), prompt);
273
274         /* Add our extra message hints, if any */
275         wrong_text = NULL;
276
277         if (applet->pw_wrong_label) {
278                 if (invalid_password) {
279                         wrong_text = g_strdup (_("The password you entered is invalid"));
280                 } else {
281                         krb5_timestamp now;
282                         int minutes_left;
283
284                         if (krb5_timeofday(kcontext, &now) == 0)
285                                 minutes_left = (creds_expiry - now) / 60;
286                         else
287                                 minutes_left = 0;
288                         wrong_text = minutes_to_expiry_text (minutes_left);
289                 }
290         }
291
292         if (wrong_text) {
293                 wrong_markup = g_strdup_printf ("<span size=\"smaller\" style=\"italic\">%s</span>", wrong_text);
294                 gtk_label_set_markup (GTK_LABEL (applet->pw_wrong_label), wrong_markup);
295                 g_free(wrong_text);
296                 g_free(wrong_markup);
297         } else {
298                 gtk_label_set_text (GTK_LABEL (applet->pw_wrong_label), "");
299         }
300         g_free (prompt);
301 }
302
303
304 static krb5_error_code
305 auth_dialog_prompter (krb5_context ctx,
306                       void *data,
307                       const char *name,
308                       const char *banner,
309                       int num_prompts,
310                       krb5_prompt prompts[])
311 {
312         Krb5AuthApplet* applet = (Krb5AuthApplet*)data;
313         krb5_error_code errcode;
314         int i;
315
316         errcode = KRB5_LIBOS_CANTREADPWD;
317         canceled = FALSE;
318         canceled_creds_expiry = 0;
319
320         for (i = 0; i < num_prompts; i++) {
321                 const gchar *password = NULL;
322                 int password_len = 0;
323                 int response;
324                 guint32 source_id = 0;
325
326                 GtkWidget *entry;
327
328                 errcode = KRB5_LIBOS_CANTREADPWD;
329
330                 krb5_auth_dialog_setup (applet, (gchar *) prompts[i].prompt, prompts[i].hidden);
331                 entry = glade_xml_get_widget (applet->pw_xml, "krb5_entry");
332                 gtk_widget_grab_focus (entry);
333
334                 source_id = g_timeout_add_seconds (5, (GSourceFunc)krb5_auth_dialog_do_updates, applet);
335                 response = gtk_dialog_run (GTK_DIALOG (applet->pw_dialog));
336                 switch (response)
337                 {
338                         case GTK_RESPONSE_OK:
339                                 password = gtk_secure_entry_get_text (GTK_SECURE_ENTRY (entry));
340                                 password_len = strlen (password);
341                                 errcode = 0;
342                                 break;
343                         case GTK_RESPONSE_CANCEL:
344                                 canceled = TRUE;
345                                 break;
346                         case GTK_RESPONSE_NONE:
347                         case GTK_RESPONSE_DELETE_EVENT:
348                                 break;
349                         default:
350                                 g_warning ("Unknown Response: %d", response);
351                                 g_assert_not_reached ();
352                 }
353
354                 g_source_remove (source_id);
355
356                 prompts[i].reply->data = (char *) password;
357                 prompts[i].reply->length = password_len;
358         }
359
360         /* Reset this, so we know the next time we get a TRUE value, it is accurate. */
361         gtk_widget_hide (applet->pw_dialog);
362         invalid_password = FALSE;
363
364         return errcode;
365 }
366
367 static gboolean is_online = TRUE;
368
369 #ifdef ENABLE_NETWORK_MANAGER
370 static void
371 network_state_cb (libnm_glib_ctx *context,
372                   gpointer data)
373 {
374         gboolean *online = (gboolean*) data;
375
376         libnm_glib_state state;
377
378         state = libnm_glib_get_network_state (context);
379
380         switch (state)
381         {
382                 case LIBNM_NO_DBUS:
383                 case LIBNM_NO_NETWORKMANAGER:
384                 case LIBNM_INVALID_CONTEXT:
385                         /* do nothing */
386                         break;
387                 case LIBNM_NO_NETWORK_CONNECTION:
388                         *online = FALSE;
389                         break;
390                 case LIBNM_ACTIVE_NETWORK_CONNECTION:
391                         *online = TRUE;
392                         break;
393         }
394 }
395 #endif
396
397
398 static gboolean
399 credentials_expiring (gpointer *data)
400 {
401         int retval;
402         gboolean give_up;
403         gboolean renewable;
404         Krb5AuthApplet* applet = (Krb5AuthApplet*) data;
405
406         KA_DEBUG("Checking expiry: %d", applet->pw_prompt_secs);
407         if (credentials_expiring_real (applet, &renewable) && is_online) {
408                 give_up = canceled && (creds_expiry == canceled_creds_expiry);
409                 if (!give_up) {
410                         do {
411                                 retval = grab_credentials (applet, renewable);
412                                 give_up = canceled &&
413                                           (creds_expiry == canceled_creds_expiry);
414                         } while ((retval != 0) && 
415                                  (retval != KRB5_REALM_CANT_RESOLVE) &&
416                                  (retval != KRB5_KDC_UNREACH) &&
417                                  invalid_password &&
418                                  !give_up);
419                 }
420         }
421         ka_update_status(applet, creds_expiry);
422         return TRUE;
423 }
424
425
426 static void
427 set_options_using_creds(const Krb5AuthApplet* applet,
428                         krb5_context context,
429                         krb5_creds *creds,
430                         krb5_get_init_creds_opt *opts)
431 {
432         krb5_deltat renew_lifetime;
433         int flag;
434
435         flag = get_cred_forwardable(creds) != 0;
436         krb5_get_init_creds_opt_set_forwardable(opts, flag);
437         flag = get_cred_proxiable(creds) != 0;
438         krb5_get_init_creds_opt_set_proxiable(opts, flag);
439         flag = get_cred_renewable(creds) != 0;
440         if (flag && (creds->times.renew_till > creds->times.starttime)) {
441                 renew_lifetime = creds->times.renew_till -
442                                  creds->times.starttime;
443                 krb5_get_init_creds_opt_set_renew_life(opts,
444                                                        renew_lifetime);
445         }
446         if (creds->times.endtime >
447             creds->times.starttime + applet->pw_prompt_secs) {
448                 krb5_get_init_creds_opt_set_tkt_life(opts,
449                                                      creds->times.endtime -
450                                                      creds->times.starttime);
451         }
452         /* This doesn't do a deep copy -- fix it later. */
453         /* krb5_get_init_creds_opt_set_address_list(opts, creds->addresses); */
454 }
455
456 static int
457 grab_credentials (Krb5AuthApplet* applet, gboolean renewable)
458 {
459         krb5_error_code retval;
460         krb5_creds my_creds;
461         krb5_ccache ccache;
462         krb5_get_init_creds_opt opts;
463
464         memset(&my_creds, 0, sizeof(my_creds));
465
466         if (kprincipal == NULL) {
467                 retval = krb5_parse_name(kcontext, applet->principal,
468                                          &kprincipal);
469                 if (retval) {
470                         return retval;
471                 }
472         }
473
474         retval = krb5_cc_default (kcontext, &ccache);
475         if (retval)
476                 return retval;
477
478         krb5_get_init_creds_opt_init (&opts);
479         if (get_tgt_from_ccache (kcontext, &my_creds)) {
480                 set_options_using_creds (applet, kcontext, &my_creds, &opts);
481                 creds_expiry = my_creds.times.endtime;
482
483                 if (renewable) {
484                         retval = get_renewed_creds (kcontext, &my_creds, kprincipal, ccache, NULL);
485
486                         /* If we succeeded in renewing the credentials, we store it. */
487                         if (retval == 0) {
488                                 goto store;
489                         }
490                         /* Else, try to get new credentials, so just fall through */
491                 }
492                 krb5_free_cred_contents (kcontext, &my_creds);
493         } else {
494                 creds_expiry = 0;
495         }
496
497         retval = krb5_get_init_creds_password(kcontext, &my_creds, kprincipal,
498                                               NULL, auth_dialog_prompter, applet,
499                                               0, NULL, &opts);
500         if (canceled) {
501                 canceled_creds_expiry = creds_expiry;
502         }
503         if (retval) {
504                 switch (retval) {
505                         case KRB5KDC_ERR_PREAUTH_FAILED:
506                         case KRB5KRB_AP_ERR_BAD_INTEGRITY:
507                                 /* Invalid password, try again. */
508                                 invalid_password = TRUE;
509                                 goto out;
510                         default:
511                                 break;
512                 }
513                 goto out;
514         }
515
516 store:
517         retval = krb5_cc_initialize(kcontext, ccache, kprincipal);
518         if (retval) {
519                 goto out;
520         }
521
522         retval = krb5_cc_store_cred(kcontext, ccache, &my_creds);
523         if (retval) {
524                 goto out;
525         }
526
527         creds_expiry = my_creds.times.endtime;
528 out:
529         krb5_free_cred_contents (kcontext, &my_creds);
530         krb5_cc_close (kcontext, ccache);
531
532         return retval;
533 }
534
535 static gboolean
536 get_tgt_from_ccache (krb5_context context, krb5_creds *creds)
537 {
538         krb5_ccache ccache;
539         krb5_creds mcreds;
540         krb5_principal principal, tgt_principal;
541         gboolean ret;
542
543         memset(&ccache, 0, sizeof(ccache));
544         ret = FALSE;
545         if (krb5_cc_default(context, &ccache) == 0) {
546                 memset(&principal, 0, sizeof(principal));
547                 if (krb5_cc_get_principal(context, ccache, &principal) == 0) {
548                         memset(&tgt_principal, 0, sizeof(tgt_principal));
549                         if (krb5_build_principal_ext(context, &tgt_principal,
550                                                      get_principal_realm_length(principal),
551                                                      get_principal_realm_data(principal),
552                                                      KRB5_TGS_NAME_SIZE,
553                                                      KRB5_TGS_NAME,
554                                                      get_principal_realm_length(principal),
555                                                      get_principal_realm_data(principal),
556                                                      0) == 0) {
557                                 memset(creds, 0, sizeof(*creds));
558                                 memset(&mcreds, 0, sizeof(mcreds));
559                                 mcreds.client = principal;
560                                 mcreds.server = tgt_principal;
561                                 if (krb5_cc_retrieve_cred(context, ccache,
562                                                           0,
563                                                           &mcreds,
564                                                           creds) == 0) {
565                                         ret = TRUE;
566                                 } else {
567                                         memset(creds, 0, sizeof(*creds));
568                                 }
569                                 krb5_free_principal(context, tgt_principal);
570                         }
571                         krb5_free_principal(context, principal);
572                 }
573                 krb5_cc_close(context, ccache);
574         }
575         return ret;
576 }
577
578 static gboolean
579 using_krb5()
580 {
581         krb5_error_code err;
582         gboolean have_tgt = FALSE;
583         krb5_creds creds;
584
585         err = krb5_init_context(&kcontext);
586         if (err) {
587                 return TRUE;
588         }
589
590         have_tgt = get_tgt_from_ccache(kcontext, &creds);
591         if (have_tgt) {
592                 krb5_copy_principal(kcontext, creds.client, &kprincipal);
593                 krb5_free_cred_contents (kcontext, &creds);
594         }
595
596         return have_tgt;
597 }
598
599
600 void
601 ka_destroy_cache (GtkMenuItem  *menuitem, gpointer data)
602 {
603         Krb5AuthApplet* applet = (Krb5AuthApplet*) data;
604         krb5_ccache  ccache;
605         const char* cache;
606         krb5_error_code ret;
607         gboolean renewable;
608
609         cache = krb5_cc_default_name(kcontext);
610         ret =  krb5_cc_resolve(kcontext, cache, &ccache);
611         ret = krb5_cc_destroy (kcontext, ccache);
612
613         credentials_expiring_real(applet, &renewable);
614 }
615
616
617 static void
618 ka_error_dialog(int err)
619 {
620         const char* msg = error_message(err);
621         GtkWidget *dialog = gtk_message_dialog_new (NULL,
622                                 GTK_DIALOG_DESTROY_WITH_PARENT,
623                                 GTK_MESSAGE_ERROR,
624                                 GTK_BUTTONS_CLOSE,
625                                 _("Couldn't acquire kerberos ticket: '%s'"), msg);
626         gtk_dialog_run (GTK_DIALOG (dialog));
627         gtk_widget_destroy (dialog);
628 }
629
630
631 /* this is done on leftclick, update the tooltip immediately */
632 void
633 ka_grab_credentials (Krb5AuthApplet* applet)
634 {
635         int retval;
636         gboolean renewable, retry;
637
638         applet->pw_dialog_persist = TRUE;
639         do {
640                 retry = TRUE;
641                 retval = grab_credentials (applet, FALSE);
642                 switch (retval) {
643                     case KRB5KRB_AP_ERR_BAD_INTEGRITY:
644                             retry = TRUE;
645                             break;
646                     case 0: /* success */
647                     case KRB5_LIBOS_CANTREADPWD: /* canceled */
648                             retry = FALSE;
649                             break;
650                     case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
651                     default:
652                             ka_error_dialog(retval);
653                             retry = FALSE;
654                             break;
655                 }
656         } while(retry);
657
658         applet->pw_dialog_persist = FALSE;
659         credentials_expiring_real(applet, &renewable);
660 }
661
662
663 static GtkWidget*
664 ka_create_gtk_secure_entry (GladeXML *xml, gchar *func_name, gchar *name,
665                                 gchar *s1, gchar *s2, gint i1, gint i2,
666                                 gpointer user_data)
667 {
668         GtkWidget* entry = NULL;
669
670         if (!strcmp(name, "krb5_entry")) {
671                 entry = gtk_secure_entry_new ();
672                 gtk_secure_entry_set_activates_default(GTK_SECURE_ENTRY(entry), TRUE);
673                 gtk_widget_show (entry);
674         } else {
675                 g_warning("Don't know anything about widget %s", name);
676         }
677         return entry;
678 }
679
680
681 static void 
682 ka_secmem_init ()
683 {
684         /* Initialize secure memory.  1 is too small, so the default size
685         will be used.  */
686         secmem_init (1);
687         secmem_set_flags (SECMEM_WARN);
688         drop_privs ();
689
690         if (atexit (secmem_term))
691                 g_error("Couln't register atexit handler");
692 }
693
694
695 int
696 main (int argc, char *argv[])
697 {
698         Krb5AuthApplet *applet;
699         GOptionContext *context;
700         GError *error = NULL;
701
702         guint status = 0;
703         gboolean run_auto = FALSE, run_always = FALSE;
704
705         const char *help_msg = "Run '" PACKAGE " --help' to see a full list of available command line options";
706         const GOptionEntry options [] = {
707                 {"auto", 'a', 0, G_OPTION_ARG_NONE, &run_auto,
708                         "Only run if an initialized ccache is found (default)", NULL},
709                 {"always", 'A', 0, G_OPTION_ARG_NONE, &run_always,
710                         "Always run", NULL},
711                 { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL }
712         };
713
714 #ifdef ENABLE_NETWORK_MANAGER
715         libnm_glib_ctx *nm_context;
716         guint32 nm_callback_id; 
717 #endif
718         context = g_option_context_new ("- Kerberos 5 credential checking");
719         g_option_context_add_main_entries (context, options, NULL);
720         g_option_context_add_group (context, gtk_get_option_group (TRUE));
721         g_option_context_parse (context, &argc, &argv, &error);
722         if (error) {
723                 g_print ("%s\n%s\n",
724                          error->message,
725                          help_msg);
726                 g_error_free (error);
727                 return 1;
728         }
729         textdomain (PACKAGE);
730         bind_textdomain_codeset (PACKAGE, "UTF-8");
731         bindtextdomain (PACKAGE, LOCALE_DIR);
732         ka_secmem_init();
733
734         if (!ka_dbus_connect (&status))
735                 exit(status);
736
737         if (run_always && !run_auto) {
738                 always_run = TRUE;
739         }
740         if (using_krb5 () || always_run) {
741                 applet = ka_create_applet ();
742                 if (!applet)
743                         return 1;
744                 if (!ka_gconf_init (applet, argc, argv))
745                         return 1;
746
747                 /* setup the pw dialog */
748                 glade_set_custom_handler (&ka_create_gtk_secure_entry, NULL);
749                 applet->pw_xml = glade_xml_new (GLADEDIR "krb5-auth-dialog.glade", NULL, NULL);
750                 applet->pw_wrong_label = glade_xml_get_widget (applet->pw_xml, "krb5_wrong_label");
751                 applet->pw_dialog = glade_xml_get_widget (applet->pw_xml, "krb5_dialog");
752
753                 g_set_application_name (_("Network Authentication"));
754                 gtk_window_set_default_icon_name (applet->icons[1]);
755
756 #ifdef ENABLE_NETWORK_MANAGER
757                 nm_context = libnm_glib_init ();
758                 if (!nm_context) {
759                         g_warning ("Could not initialize libnm_glib");
760                 } else {
761                         nm_callback_id = libnm_glib_register_callback (nm_context, network_state_cb, &is_online, NULL);
762                         if (nm_callback_id == 0) {
763                                 libnm_glib_shutdown (nm_context);
764                                 nm_context = NULL;
765
766                                 g_warning ("Could not connect to NetworkManager, connection status will not be managed!");
767                         }
768                 }
769 #endif /* ENABLE_NETWORK_MANAGER */
770
771                 if (credentials_expiring ((gpointer)applet)) {
772                         g_timeout_add_seconds (CREDENTIAL_CHECK_INTERVAL, (GSourceFunc)credentials_expiring, applet);
773                 }
774                 gtk_main ();
775         }
776
777         return 0;
778 }