chiark / gitweb /
ask-password: add minimal framework to allow services query SSL/harddisk passphrases...
[elogind.git] / src / ask-password-agent.vala
diff --git a/src/ask-password-agent.vala b/src/ask-password-agent.vala
new file mode 100644 (file)
index 0000000..5355bb4
--- /dev/null
@@ -0,0 +1,250 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+using Gtk;
+using GLib;
+using DBus;
+using Linux;
+using Posix;
+
+[CCode (cheader_filename = "time.h")]
+extern int clock_gettime(int id, out timespec ts);
+
+public class PasswordDialog : Dialog {
+
+        public Entry entry;
+
+        public PasswordDialog(string message, string icon) {
+                set_title("System Password");
+                set_has_separator(false);
+                set_border_width(8);
+                set_default_response(ResponseType.OK);
+                set_icon_name(icon);
+
+                add_button(STOCK_CANCEL, ResponseType.CANCEL);
+                add_button(STOCK_OK, ResponseType.OK);
+
+                Container content = (Container) get_content_area();
+
+                Box hbox = new HBox(false, 16);
+                hbox.set_border_width(8);
+                content.add(hbox);
+
+                Image image = new Image.from_icon_name(icon, IconSize.DIALOG);
+                hbox.pack_start(image, false, false);
+
+                Box vbox = new VBox(false, 8);
+                hbox.pack_start(vbox, true, true);
+
+                Label label = new Label(message);
+                vbox.pack_start(label, false, false);
+
+                entry = new Entry();
+                entry.set_visibility(false);
+                entry.set_activates_default(true);
+                vbox.pack_start(entry, false, false);
+
+                entry.activate.connect(on_entry_activated);
+
+                show_all();
+        }
+
+        public void on_entry_activated() {
+                response(ResponseType.OK);
+        }
+}
+
+public class MyStatusIcon : StatusIcon {
+
+        File directory;
+        File current;
+        FileMonitor file_monitor;
+
+        string message;
+        string icon;
+        string socket;
+
+        PasswordDialog password_dialog;
+
+        public MyStatusIcon() throws GLib.Error {
+                GLib.Object(icon_name : "dialog-password");
+                set_title("System Password Agent");
+
+                directory = File.new_for_path("/dev/.systemd/ask-password/");
+                file_monitor = directory.monitor_directory(0);
+                file_monitor.changed.connect(file_monitor_changed);
+
+                current = null;
+                look_for_password();
+
+                activate.connect(status_icon_activate);
+        }
+
+        void file_monitor_changed(GLib.File file, GLib.File? other_file, GLib.FileMonitorEvent event_type) throws GLib.Error {
+
+                if (!file.get_basename().has_prefix("ask."))
+                        return;
+
+                if (event_type == FileMonitorEvent.CREATED ||
+                    event_type == FileMonitorEvent.DELETED)
+                        look_for_password();
+        }
+
+        void look_for_password() throws GLib.Error {
+
+                if (current != null) {
+                        if (!current.query_exists()) {
+                                current = null;
+                                if (password_dialog != null)
+                                        password_dialog.response(ResponseType.REJECT);
+                        }
+                }
+
+                if (current == null) {
+                        FileEnumerator enumerator = directory.enumerate_children("standard::name", FileQueryInfoFlags.NOFOLLOW_SYMLINKS);
+
+                        FileInfo i;
+                        while ((i = enumerator.next_file()) != null) {
+                                if (!i.get_name().has_prefix("ask."))
+                                        continue;
+
+                                current = directory.get_child(i.get_name());
+
+                                if (load_password())
+                                        break;
+
+                                current = null;
+                        }
+                }
+
+                if (current == null)
+                        set_visible(false);
+
+        }
+
+        bool load_password()  {
+
+                KeyFile key_file = new KeyFile();
+
+                try {
+                        timespec ts;
+
+                        key_file.load_from_file(current.get_path(), KeyFileFlags.NONE);
+
+                        string not_after_as_string = key_file.get_string("Ask", "NotAfter");
+
+                        clock_gettime(1, out ts);
+                        uint64 now = (ts.tv_sec * 1000000) + (ts.tv_nsec / 1000);
+
+                        uint64 not_after;
+                        if (not_after_as_string.scanf("%llu", out not_after) != 1)
+                                return false;
+
+                        if (not_after < now)
+                                return false;
+
+                        socket = key_file.get_string("Ask", "Socket");
+                } catch (GLib.Error e) {
+                        return false;
+                }
+
+                try {
+                        message = key_file.get_string("Ask", "Message").compress();
+                } catch (GLib.Error e) {
+                        message = "Please Enter System Password!";
+                }
+                set_tooltip_text(message);
+
+                try {
+                        icon = key_file.get_string("Ask", "Icon");
+                } catch (GLib.Error e) {
+                        icon = "dialog-password";
+                }
+                set_from_icon_name(icon);
+
+                set_visible(true);
+                return true;
+        }
+
+        void status_icon_activate() throws GLib.Error {
+
+                if (current == null)
+                        return;
+
+                if (password_dialog != null) {
+                        password_dialog.present();
+                        return;
+                }
+
+                password_dialog = new PasswordDialog(message, icon);
+
+                int result = password_dialog.run();
+                string password = password_dialog.entry.get_text();
+
+                password_dialog.destroy();
+                password_dialog = null;
+
+                if (result == ResponseType.REJECT ||
+                    result == ResponseType.DELETE_EVENT)
+                        return;
+
+                int to_process;
+
+                Process.spawn_async_with_pipes(
+                                null,
+                                { "/usr/bin/pkexec", "/lib/systemd/systemd-reply-password", result == ResponseType.OK ? "1" : "0", socket },
+                                null,
+                                0,
+                                null,
+                                null,
+                                out to_process,
+                                null,
+                                null);
+
+                OutputStream stream = new UnixOutputStream(to_process, true);
+
+                stream.write(password, password.length, null);
+        }
+}
+
+static const OptionEntry entries[] = {
+        { null }
+};
+
+void show_error(string e) {
+        var m = new MessageDialog(null, 0, MessageType.ERROR, ButtonsType.CLOSE, "%s", e);
+        m.run();
+        m.destroy();
+}
+
+int main(string[] args) {
+        try {
+                Gtk.init_with_args(ref args, "[OPTION...]", entries, "systemd-ask-password-agent");
+
+                MyStatusIcon i = new MyStatusIcon();
+                Gtk.main();
+
+        } catch (DBus.Error e) {
+                show_error(e.message);
+        } catch (GLib.Error e) {
+                show_error(e.message);
+        }
+
+        return 0;
+}