diff mbox series

[v2,12/13] ui/ncurses: Add nc-auth and authenticate when required.

Message ID 20181128042012.25916-13-sam@mendozajonas.com
State Accepted
Headers show
Series User support and client permissions | expand

Commit Message

Sam Mendoza-Jonas Nov. 28, 2018, 4:20 a.m. UTC
When the user tries to perform actions that require authentication a new
subscreen 'nc-auth' is launched which accepts a password and will send
an authentication request before performing the action.
This also adds a button in nc-config which launches an nc-auth screen
allowing the user to set or change the system password.

Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com>
---
 ui/ncurses/Makefile.am  |   4 +-
 ui/ncurses/nc-add-url.c |  63 ++++++---
 ui/ncurses/nc-auth.c    | 299 ++++++++++++++++++++++++++++++++++++++++
 ui/ncurses/nc-auth.h    |  33 +++++
 ui/ncurses/nc-config.c  |  64 +++++++--
 ui/ncurses/nc-cui.c     | 151 +++++++++++++++++++-
 ui/ncurses/nc-cui.h     |   6 +
 ui/ncurses/nc-lang.c    | 127 +++++++++++------
 ui/ncurses/nc-plugin.c  |  44 +++---
 ui/ncurses/nc-plugin.h  |   2 -
 ui/ncurses/nc-scr.h     |   1 +
 ui/ncurses/nc-widgets.c |  12 +-
 ui/ncurses/nc-widgets.h |   3 +
 13 files changed, 704 insertions(+), 105 deletions(-)
 create mode 100644 ui/ncurses/nc-auth.c
 create mode 100644 ui/ncurses/nc-auth.h
diff mbox series

Patch

diff --git a/ui/ncurses/Makefile.am b/ui/ncurses/Makefile.am
index b791b9dc..cd525dfe 100644
--- a/ui/ncurses/Makefile.am
+++ b/ui/ncurses/Makefile.am
@@ -57,7 +57,9 @@  ui_ncurses_libpbnc_la_SOURCES = \
 	ui/ncurses/nc-plugin.c \
 	ui/ncurses/nc-plugin.h \
 	ui/ncurses/nc-plugin-help.c \
-	ui/ncurses/nc-plugin-menu-help.c
+	ui/ncurses/nc-plugin-menu-help.c \
+	ui/ncurses/nc-auth.c \
+	ui/ncurses/nc-auth.h
 
 sbin_PROGRAMS += ui/ncurses/petitboot-nc
 
diff --git a/ui/ncurses/nc-add-url.c b/ui/ncurses/nc-add-url.c
index 4abca38a..33f502da 100644
--- a/ui/ncurses/nc-add-url.c
+++ b/ui/ncurses/nc-add-url.c
@@ -28,6 +28,7 @@ 
 #include <i18n/i18n.h>
 #include <log/log.h>
 
+#include "ui/common/discover-client.h"
 #include "nc-cui.h"
 #include "nc-add-url.h"
 #include "nc-widgets.h"
@@ -111,14 +112,37 @@  static void add_url_screen_process_key(struct nc_scr *scr, int key)
 		cui_show_help(screen->cui, _("Retrieve Config"),
 			&add_url_help_text);
 
-	} else if (handled) {
+	} else if (handled && (screen->cui->current == scr)) {
 		pad_refresh(screen);
 	}
 }
 
+static int screen_process_form(struct add_url_screen *screen)
+{
+	char *url;
+	int rc;
+
+	url = widget_textbox_get_value(screen->widgets.url_f);
+	if (!url || !strlen(url))
+		return 0;
+
+	/* Once we have all the info we need, tell the server */
+	rc = cui_send_url(screen->cui, url);
+
+	if (rc)
+		pb_log("cui_send_retreive failed!\n");
+	else
+		pb_debug("add_url url sent!\n");
+	return 0;
+}
+
 static int add_url_screen_post(struct nc_scr *scr)
 {
 	struct add_url_screen *screen = add_url_screen_from_scr(scr);
+
+	if (screen->exit)
+		screen->on_exit(screen->cui);
+
 	widgetset_post(screen->widgetset);
 	nc_scr_frame_draw(scr);
 	if (screen->need_redraw) {
@@ -142,34 +166,29 @@  struct nc_scr *add_url_screen_scr(struct add_url_screen *screen)
 	return &screen->scr;
 }
 
-static int screen_process_form(struct add_url_screen *screen)
+static void add_url_process_cb(struct nc_scr *scr)
 {
-	char *url;
-	int rc;
-
-	url = widget_textbox_get_value(screen->widgets.url_f);
-	if (!url || !strlen(url))
-		return 0;
-
-	/* Once we have all the info we need, tell the server */
-	rc = cui_send_url(screen->cui, url);
+	struct add_url_screen *screen = add_url_screen_from_scr(scr);
 
-	if (rc)
-		pb_log("cui_send_retreive failed!\n");
-	else
-		pb_debug("add_url url sent!\n");
-	return 0;
+	if (!screen_process_form(screen))
+		screen->exit = true;
 }
 
 static void ok_click(void *arg)
 {
 	struct add_url_screen *screen = arg;
-	if (screen_process_form(screen))
-		/* errors are written to the status line, so we'll need
-		 * to refresh */
-		wrefresh(screen->scr.main_ncw);
-	else
-		screen->exit = true;
+
+	if (discover_client_authenticated(screen->cui->client)) {
+		if (screen_process_form(screen))
+			/* errors are written to the status line, so we'll need
+			 * to refresh */
+			wrefresh(screen->scr.main_ncw);
+		else
+			screen->exit = true;
+	} else {
+		cui_show_auth(screen->cui, screen->scr.main_ncw, false,
+				add_url_process_cb);
+	}
 }
 
 static void help_click(void *arg)
diff --git a/ui/ncurses/nc-auth.c b/ui/ncurses/nc-auth.c
new file mode 100644
index 00000000..5bfda8b4
--- /dev/null
+++ b/ui/ncurses/nc-auth.c
@@ -0,0 +1,299 @@ 
+/*
+ *  Copyright (C) 2018 IBM Corporation
+ *
+ *  This program 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; version 2 of the License.
+ *
+ *  This program 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
+ */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <talloc/talloc.h>
+#include <types/types.h>
+#include <i18n/i18n.h>
+#include <log/log.h>
+
+#include "nc-cui.h"
+#include "nc-widgets.h"
+#include "nc-auth.h"
+
+#define N_FIELDS        5
+
+struct auth_screen {
+	struct nc_scr		scr;
+	struct cui		*cui;
+	struct nc_scr 		*return_scr;
+	struct nc_widgetset 	*widgetset;
+	void 			(*process_key)(struct nc_scr *, int);
+
+	bool			set_password;
+	void			(*callback)(struct nc_scr *);
+	int			offset_y;
+	int			label_x;
+	int			field_x;
+
+	bool			exit;
+	void			(*on_exit)(struct cui *);
+
+	struct {
+		struct nc_widget_label		*title_a_l;
+		struct nc_widget_label		*title_b_l;
+		struct nc_widget_textbox	*password_f;
+		struct nc_widget_label		*new_l;
+		struct nc_widget_textbox	*new_f;
+		struct nc_widget_button		*ok_b;
+		struct nc_widget_button		*cancel_b;
+	} widgets;
+};
+
+struct nc_scr *auth_screen_return_scr(struct auth_screen *screen)
+{
+	return screen->return_scr;
+}
+
+struct nc_scr *auth_screen_scr(struct auth_screen *screen)
+{
+	return &screen->scr;
+}
+
+static struct auth_screen *auth_screen_from_scr(struct nc_scr *scr)
+{
+	struct auth_screen *auth_screen;
+
+	assert(scr->sig == pb_auth_screen_sig);
+	auth_screen = (struct auth_screen *)
+		((char *)scr - (size_t)&((struct auth_screen *)0)->scr);
+	assert(auth_screen->scr.sig == pb_auth_screen_sig);
+	return auth_screen;
+}
+
+static void auth_screen_process_key(struct nc_scr *scr, int key)
+{
+	struct auth_screen *screen = auth_screen_from_scr(scr);
+	bool handled;
+
+	handled = widgetset_process_key(screen->widgetset, key);
+
+	if (!handled) {
+		switch (key) {
+		case 'x':
+		case 27: /* esc */
+			screen->exit = true;
+			break;
+		}
+	}
+
+	if (screen->exit)
+		screen->on_exit(screen->cui);
+	else if (handled)
+		wrefresh(screen->scr.sub_ncw);
+}
+
+static void auth_screen_frame_draw(struct nc_scr *scr)
+{
+	int y, x;
+
+	getmaxyx(scr->sub_ncw, y, x);
+
+	mvwhline(scr->sub_ncw, 0, 0, ACS_HLINE, x);
+	mvwhline(scr->sub_ncw, y - 1, 0, ACS_HLINE, x);
+
+	mvwvline(scr->sub_ncw, 0, 0, ACS_VLINE, y);
+	mvwvline(scr->sub_ncw, 0, x - 1, ACS_VLINE, y);
+}
+
+static int auth_screen_post(struct nc_scr *scr)
+{
+	struct auth_screen *screen = auth_screen_from_scr(scr);
+	widgetset_post(screen->widgetset);
+	auth_screen_frame_draw(scr);
+	wrefresh(scr->sub_ncw);
+	return 0;
+}
+
+static int auth_screen_unpost(struct nc_scr *scr)
+{
+	struct auth_screen *screen = auth_screen_from_scr(scr);
+	widgetset_unpost(screen->widgetset);
+	return 0;
+}
+
+static void ok_click(void *arg)
+{
+	struct auth_screen *screen = arg;
+	char *password, *new_password;
+	int rc;
+
+
+	password = widget_textbox_get_value(screen->widgets.password_f);
+	if (screen->set_password) {
+		new_password = widget_textbox_get_value(screen->widgets.new_f);
+		rc = cui_send_set_password(screen->cui, password, new_password);
+	} else
+		rc = cui_send_authenticate(screen->cui, password);
+
+	if (rc)
+		pb_log("Failed to send authenticate action\n");
+	else if (screen->callback)
+		screen->callback(screen->return_scr);
+
+	screen->exit = true;
+}
+
+static void cancel_click(void *arg)
+{
+	struct auth_screen *screen = arg;
+	screen->exit = true;
+}
+
+static void auth_screen_layout_widgets(struct auth_screen *screen)
+{
+	int y = 1;
+
+	widget_move(widget_label_base(screen->widgets.title_a_l),
+			y++, screen->label_x);
+	widget_move(widget_label_base(screen->widgets.title_b_l),
+			y++, screen->label_x);
+
+	y += 1;
+
+	widget_move(widget_textbox_base(screen->widgets.password_f),
+			y++, screen->field_x);
+
+	y += 1;
+
+	if (screen->set_password) {
+		widget_move(widget_label_base(screen->widgets.new_l),
+				y++, screen->label_x);
+		widget_move(widget_textbox_base(screen->widgets.new_f),
+				y++, screen->field_x);
+		y += 1;
+	}
+
+	widget_move(widget_button_base(screen->widgets.ok_b),
+			y, 10);
+	widget_move(widget_button_base(screen->widgets.cancel_b),
+			y, 30);
+}
+
+static void auth_screen_draw(struct auth_screen *screen)
+{
+	struct nc_widgetset *set;
+
+	set = widgetset_create(screen, screen->scr.main_ncw,
+			screen->scr.sub_ncw);
+	if (!set) {
+		pb_log("%s: failed to create widgetset\n", __func__);
+		return;
+	}
+	screen->widgetset = set;
+
+	screen->widgets.title_a_l = widget_new_label(set, 0, 0,
+			_("This action requires authorisation."));
+	screen->widgets.title_b_l = widget_new_label(set, 0, 0,
+			_("Please enter the system password."));
+
+	screen->widgets.password_f = widget_new_textbox_hidden(set, 0, 0,
+			COLS - 20 - 20, "", true);
+
+	if (screen->set_password) {
+		screen->widgets.new_l = widget_new_label(set, 0, 0,
+				_("New password:"));
+		screen->widgets.new_f = widget_new_textbox_hidden(set, 0, 0,
+				COLS - 20 - 20, "", true);
+	}
+
+	screen->widgets.ok_b = widget_new_button(set, 0, 0, 10, _("OK"),
+			ok_click, screen);
+	screen->widgets.cancel_b = widget_new_button(set, 0, 0, 10, _("Cancel"),
+			cancel_click, screen);
+
+	auth_screen_layout_widgets(screen);
+}
+
+static int auth_screen_destroy(void *arg)
+{
+	struct auth_screen *screen = arg;
+	if (screen->scr.sub_ncw)
+		delwin(screen->scr.sub_ncw);
+	return 0;
+}
+
+struct auth_screen *auth_screen_init(struct cui *cui,
+		WINDOW *parent, bool set_password,
+		void (*callback)(struct nc_scr *),
+		void (*on_exit)(struct cui *))
+{
+	struct auth_screen *screen = NULL;
+	struct nc_scr *scr;
+	int y, x;
+
+	if (!cui || !parent)
+		return NULL;
+
+	screen = talloc_zero(cui, struct auth_screen);
+	if (!screen)
+		return NULL;
+	talloc_set_destructor(screen, auth_screen_destroy);
+
+	screen->cui = cui;
+	screen->return_scr = cui->current;
+	screen->set_password = set_password;
+	screen->callback = callback;
+	screen->on_exit = on_exit;
+	screen->label_x = 5;
+	screen->field_x = 10;
+
+	/*
+	 * Manually init our nc_scr: we only want to create the subwin and
+	 * 'inherit' the parent window.
+	 */
+	scr = &screen->scr;
+	scr->sig = pb_auth_screen_sig;
+	scr->ui_ctx = cui;
+	scr->process_key = auth_screen_process_key;
+	scr->post = auth_screen_post;
+	scr->unpost = auth_screen_unpost;
+	scr->resize = NULL;
+
+
+	getbegyx(parent, y, x);
+	/* Hold on to the real offset from the top of the screen */
+	screen->offset_y = y + 5;
+	(void)x;
+
+	scr->main_ncw = parent;
+	scr->sub_ncw = derwin(parent, set_password ? 15 : 10, COLS - 20,
+			5, 10);	/* relative to parent origin */
+	if (!scr->sub_ncw) {
+		pb_log("Could not create subwin\n");
+		goto err;
+	}
+
+	auth_screen_draw(screen);
+
+	return screen;
+err:
+	pb_log("failed to create auth screen\n");
+	if (screen) {
+		if (screen->scr.sub_ncw)
+			delwin(screen->scr.sub_ncw);
+		talloc_free(screen);
+	}
+	return NULL;
+}
diff --git a/ui/ncurses/nc-auth.h b/ui/ncurses/nc-auth.h
new file mode 100644
index 00000000..e8e41482
--- /dev/null
+++ b/ui/ncurses/nc-auth.h
@@ -0,0 +1,33 @@ 
+/*
+ *  Copyright (C) 2018 IBM Corporation
+ *
+ *  This program 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; version 2 of the License.
+ *
+ *  This program 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
+ */
+
+#ifndef _NC_AUTH_H
+#define _NC_AUTH_H
+
+#include "nc-cui.h"
+
+struct auth_screen;
+
+struct auth_screen *auth_screen_init(struct cui *cui,
+		WINDOW *pad, bool set_password,
+		void (callback)(struct nc_scr *),
+		void (*on_exit)(struct cui *));
+
+struct nc_scr *auth_screen_scr(struct auth_screen *screen);
+struct nc_scr *auth_screen_return_scr(struct auth_screen *screen);
+
+#endif /* define _NC_AUTH_H */
diff --git a/ui/ncurses/nc-config.c b/ui/ncurses/nc-config.c
index 51861763..4685fa5d 100644
--- a/ui/ncurses/nc-config.c
+++ b/ui/ncurses/nc-config.c
@@ -29,11 +29,12 @@ 
 #include <log/log.h>
 #include <i18n/i18n.h>
 
+#include "ui/common/discover-client.h"
 #include "nc-cui.h"
 #include "nc-config.h"
 #include "nc-widgets.h"
 
-#define N_FIELDS	48
+#define N_FIELDS	49
 
 extern struct help_text config_help_text;
 
@@ -51,7 +52,6 @@  struct config_screen {
 
 	bool			exit;
 	bool			show_help;
-	bool			show_subset;
 	bool			need_redraw;
 	bool			need_update;
 
@@ -118,6 +118,8 @@  struct config_screen {
 		struct nc_widget_label		*manual_console_l;
 		struct nc_widget_label		*current_console_l;
 
+		struct nc_widget_button		*update_password_l;
+
 		struct nc_widget_label		*net_override_l;
 		struct nc_widget_label		*safe_mode;
 		struct nc_widget_button		*ok_b;
@@ -175,7 +177,7 @@  static void config_screen_process_key(struct nc_scr *scr, int key)
 		cui_show_help(screen->cui, _("System Configuration"),
 				&config_help_text);
 
-	} else if (handled && !screen->show_subset) {
+	} else if (handled && (screen->cui->current == scr)) {
 		pad_refresh(screen);
 	}
 }
@@ -370,15 +372,39 @@  static int screen_process_form(struct config_screen *screen)
 	return 0;
 }
 
+static void config_screen_config_cb(struct nc_scr *scr)
+{
+	struct config_screen *screen = config_screen_from_scr(scr);
+
+	if (!screen_process_form(screen))
+		screen->exit = true;
+}
+
+#ifdef CRYPT_SUPPORT
+static void password_click(void *arg)
+{
+	struct config_screen *screen = arg;
+
+	screen->need_update = true;
+	cui_show_auth(screen->cui, screen->scr.main_ncw, true, NULL);
+}
+#endif
+
 static void ok_click(void *arg)
 {
 	struct config_screen *screen = arg;
-	if (screen_process_form(screen))
-		/* errors are written to the status line, so we'll need
-		 * to refresh */
-		wrefresh(screen->scr.main_ncw);
-	else
-		screen->exit = true;
+
+	if (discover_client_authenticated(screen->cui->client)) {
+		if (screen_process_form(screen))
+			/* errors are written to the status line, so we'll need
+			 * to refresh */
+			wrefresh(screen->scr.main_ncw);
+		else
+			screen->exit = true;
+	} else {
+		cui_show_auth(screen->cui, screen->scr.main_ncw, false,
+				config_screen_config_cb);
+	}
 }
 
 static void help_click(void *arg)
@@ -650,6 +676,12 @@  static void config_screen_layout_widgets(struct config_screen *screen)
 					screen->widgets.current_console_l), false);
 	}
 
+#ifdef CRYPT_SUPPORT
+	widget_move(widget_button_base(screen->widgets.update_password_l),
+			y, screen->field_x);
+	y += 2;
+#endif
+
 	if (screen->net_override) {
 		widget_move(widget_label_base(screen->widgets.net_override_l),
 				y, screen->label_x);
@@ -705,7 +737,6 @@  static void config_screen_add_device(void *arg)
 {
 	struct config_screen *screen = arg;
 
-	screen->show_subset = true;
 	cui_show_subset(screen->cui, _("Select a boot device to add"),
 			screen->widgets.boot_order_f);
 }
@@ -1113,6 +1144,11 @@  static void config_screen_setup_widgets(struct config_screen *screen,
 				ttyname(STDIN_FILENO));
 	screen->widgets.current_console_l = widget_new_label(set, 0 , 0, tty);
 
+#ifdef CRYPT_SUPPORT
+	screen->widgets.update_password_l = widget_new_button(set, 0, 0, 30,
+			_("Update system password"), password_click, screen);
+#endif
+
 	screen->widgets.ok_b = widget_new_button(set, 0, 0, 10, _("OK"),
 			ok_click, screen);
 	screen->widgets.help_b = widget_new_button(set, 0, 0, 10, _("Help"),
@@ -1212,7 +1248,12 @@  void config_screen_update(struct config_screen *screen,
 static int config_screen_post(struct nc_scr *scr)
 {
 	struct config_screen *screen = config_screen_from_scr(scr);
-	screen->show_subset = false;
+
+	/* We may have been posted after an auth action completed */
+	if (screen->exit) {
+		screen->on_exit(screen->cui);
+		return 0;
+	}
 
 	if (screen->need_update) {
 		config_screen_draw(screen, screen->cui->config,
@@ -1262,7 +1303,6 @@  struct config_screen *config_screen_init(struct cui *cui,
 	screen->field_x = 17;
 
 	screen->ipmi_override = false;
-	screen->show_subset = false;
 
 	screen->scr.frame.ltitle = talloc_strdup(screen,
 			_("Petitboot System Configuration"));
diff --git a/ui/ncurses/nc-cui.c b/ui/ncurses/nc-cui.c
index 3c754546..88b8a91c 100644
--- a/ui/ncurses/nc-cui.c
+++ b/ui/ncurses/nc-cui.c
@@ -47,6 +47,7 @@ 
 #include "nc-statuslog.h"
 #include "nc-subset.h"
 #include "nc-plugin.h"
+#include "nc-auth.h"
 #include "console-codes.h"
 
 extern const struct help_text main_menu_help_text;
@@ -307,6 +308,26 @@  static int cui_boot(struct pmenu_item *item)
 	return 0;
 }
 
+static void cui_boot_cb(struct nc_scr *scr)
+{
+	struct pmenu *menu = pmenu_from_scr(scr);
+
+	if (pmenu_find_selected(menu))
+		cui_boot(pmenu_find_selected(menu));
+}
+
+static int cui_boot_check(struct pmenu_item *item)
+{
+	struct cui *cui = cui_from_item(item);
+
+	if (discover_client_authenticated(cui->client))
+		return cui_boot(item);
+
+	cui_show_auth(cui, item->pmenu->scr.main_ncw, false, cui_boot_cb);
+
+	return 0;
+}
+
 static void cui_boot_editor_on_exit(struct cui *cui,
 		struct pmenu_item *item,
 		struct pb_boot_data *bd)
@@ -338,7 +359,7 @@  static void cui_boot_editor_on_exit(struct cui *cui,
 		}
 
 		item->on_edit = cui_item_edit;
-		item->on_execute = cui_boot;
+		item->on_execute = cui_boot_check;
 		item->data = cod;
 
 		talloc_steal(item, cod);
@@ -397,6 +418,52 @@  void cui_item_new(struct pmenu *menu)
 	cui_set_current(cui, boot_editor_scr(cui->boot_editor));
 }
 
+
+/* Call pb-plugin to install a plugin specified by plugin_file */
+static int cui_install_plugin(struct pmenu_item *item)
+{
+	struct cui *cui = cui_from_item(item);
+	struct cui_opt_data *cod = cod_from_item(item);
+	int rc;
+
+	rc = cui_send_plugin_install(cui, cod->pd->plugin_file);
+
+	if (rc) {
+		pb_log("cui_send_plugin_install failed!\n");
+		nc_scr_status_printf(cui->current,
+				_("Failed to send install request"));
+	} else {
+		nc_scr_status_printf(cui->current, _("Installing plugin %s"),
+				cod->pd->plugin_file);
+		pb_debug("cui_send_plugin_install sent!\n");
+	}
+
+	return rc;
+}
+
+static void cui_plugin_install_cb(struct nc_scr *scr)
+{
+	struct pmenu *menu = pmenu_from_scr(scr);
+
+	if (pmenu_find_selected(menu))
+		cui_install_plugin(pmenu_find_selected(menu));
+	else
+		pb_debug("%s: no current item\n", __func__);
+}
+
+static int cui_plugin_install_check(struct pmenu_item *item)
+{
+	struct cui *cui = cui_from_item(item);
+
+	if (discover_client_authenticated(cui->client))
+		return cui_install_plugin(item);
+
+	cui_show_auth(cui, item->pmenu->scr.main_ncw, false,
+			cui_plugin_install_cb);
+
+	return 0;
+}
+
 static void cui_sysinfo_exit(struct cui *cui)
 {
 	cui_set_current(cui, &cui->main->scr);
@@ -535,6 +602,39 @@  void cui_show_subset(struct cui *cui, const char *title,
 		cui_set_current(cui, subset_screen_scr(cui->subset_screen));
 }
 
+static void cui_auth_exit(struct cui *cui)
+{
+	struct nc_scr *return_scr = auth_screen_return_scr(cui->auth_screen);
+
+	/*
+	 * Destroy the auth screen first so that the subwindow is cleaned up
+	 * before the return_scr posts. If we don't do this operations on the
+	 * main_ncw can cause a blank screen at first (eg. status update).
+	 */
+	nc_scr_unpost(cui->current);
+	talloc_free(cui->auth_screen);
+	cui->auth_screen = NULL;
+
+	cui->current = return_scr;
+	nc_scr_post(cui->current);
+}
+
+void cui_show_auth(struct cui *cui, WINDOW *parent, bool set_password,
+		void (*callback)(struct nc_scr *))
+{
+	if (!cui->current)
+		return;
+
+	if (cui->auth_screen)
+		return;
+
+	cui->auth_screen = auth_screen_init(cui, parent, set_password,
+			callback, cui_auth_exit);
+
+	if (cui->auth_screen)
+		cui_set_current(cui, auth_screen_scr(cui->auth_screen));
+}
+
 /**
  * cui_set_current - Set the currently active screen and redraw it.
  */
@@ -769,10 +869,10 @@  static int cui_boot_option_add(struct device *dev, struct boot_option *opt,
 
 	if (plugin_option) {
 		i->on_edit = NULL;
-		i->on_execute = plugin_install_plugin;
+		i->on_execute = cui_plugin_install_check;
 	} else {
 		i->on_edit = cui_item_edit;
-		i->on_execute = cui_boot;
+		i->on_execute = cui_boot_check;
 	}
 
 	i->data = cod = talloc(i, struct cui_opt_data);
@@ -1202,7 +1302,7 @@  static void cui_update_sysinfo(struct system_info *sysinfo, void *arg)
 	cui_update_mm_title(cui);
 }
 
-static void cui_update_language(struct cui *cui, char *lang)
+void cui_update_language(struct cui *cui, const char *lang)
 {
 	bool repost_menu;
 	char *cur_lang;
@@ -1266,6 +1366,17 @@  int cui_send_plugin_install(struct cui *cui, char *file)
 	return discover_client_send_plugin_install(cui->client, file);
 }
 
+int cui_send_authenticate(struct cui *cui, char *password)
+{
+	return discover_client_send_authenticate(cui->client, password);
+}
+
+int cui_send_set_password(struct cui *cui, char *password, char *new_password)
+{
+	return discover_client_send_set_password(cui->client, password,
+			new_password);
+}
+
 void cui_send_reinit(struct cui *cui)
 {
 	discover_client_send_reinit(cui->client);
@@ -1295,10 +1406,38 @@  static int menu_statuslog_execute(struct pmenu_item *item)
 	return 0;
 }
 
+static void menu_reinit_cb(struct nc_scr *scr)
+{
+	struct pmenu *menu = pmenu_from_scr(scr);
+
+	cui_send_reinit(cui_from_pmenu(menu));
+}
+
 static int menu_reinit_execute(struct pmenu_item *item)
 {
-	if (cui_from_item(item)->client)
-		cui_send_reinit(cui_from_item(item));
+	struct cui *cui = cui_from_item(item);
+
+	if (!cui->client)
+		return 0;
+
+	/* If we don't need to authenticate, send the reinit immediately */
+	if (discover_client_authenticated(cui->client)) {
+		cui_send_reinit(cui);
+		return 0;
+	}
+
+	if (!cui->current)
+		return 0;
+
+	if (cui->auth_screen)
+		return 0;
+
+	cui->auth_screen = auth_screen_init(cui, cui->current->main_ncw,
+			false, menu_reinit_cb, cui_auth_exit);
+
+	if (cui->auth_screen)
+		cui_set_current(cui, auth_screen_scr(cui->auth_screen));
+
 	return 0;
 }
 
diff --git a/ui/ncurses/nc-cui.h b/ui/ncurses/nc-cui.h
index abe4db98..8fa27aa7 100644
--- a/ui/ncurses/nc-cui.h
+++ b/ui/ncurses/nc-cui.h
@@ -73,6 +73,7 @@  struct cui {
 	struct help_screen *help_screen;
 	struct subset_screen *subset_screen;
 	struct statuslog_screen *statuslog_screen;
+	struct auth_screen *auth_screen;
 	struct pjs *pjs;
 	void *platform_info;
 	unsigned int default_item;
@@ -98,9 +99,13 @@  void cui_show_subset(struct cui *cui, const char *title,
 void cui_show_add_url(struct cui *cui);
 void cui_show_plugin(struct pmenu_item *item);
 void cui_show_plugin_menu(struct cui *cui);
+void cui_show_auth(struct cui *cui, WINDOW *parent, bool set_password,
+		void (*callback)(struct nc_scr *));
 int cui_send_config(struct cui *cui, struct config *config);
 int cui_send_url(struct cui *cui, char *url);
 int cui_send_plugin_install(struct cui *cui, char *file);
+int cui_send_authenticate(struct cui *cui, char *password);
+int cui_send_set_password(struct cui *cui, char *password, char *new_password);
 void cui_send_reinit(struct cui *cui);
 
 /* convenience routines */
@@ -112,6 +117,7 @@  void cui_abort_on_exit(struct pmenu *menu);
 void cui_on_open(struct pmenu *menu);
 int cui_run_cmd(struct cui *cui, const char **cmd_argv);
 int cui_run_cmd_from_item(struct pmenu_item *item);
+void cui_update_language(struct cui *cui, const char *lang);
 
 static inline struct cui *cui_from_arg(void *arg)
 {
diff --git a/ui/ncurses/nc-lang.c b/ui/ncurses/nc-lang.c
index a7c9ccc5..91d86e10 100644
--- a/ui/ncurses/nc-lang.c
+++ b/ui/ncurses/nc-lang.c
@@ -29,11 +29,12 @@ 
 #include <i18n/i18n.h>
 #include <pb-config/pb-config.h>
 
+#include "ui/common/discover-client.h"
 #include "nc-cui.h"
 #include "nc-lang.h"
 #include "nc-widgets.h"
 
-#define N_FIELDS	5
+#define N_FIELDS	7
 
 static struct lang {
 	const char	*name;
@@ -70,6 +71,9 @@  struct lang_screen {
 		struct nc_widget_select		*lang_f;
 		struct nc_widget_label		*lang_l;
 
+		struct nc_widget_label		*save_l;
+		struct nc_widget_checkbox	*save_cb;
+
 		struct nc_widget_label		*safe_mode;
 		struct nc_widget_button		*ok_b;
 		struct nc_widget_button		*cancel_b;
@@ -115,12 +119,55 @@  static void lang_screen_process_key(struct nc_scr *scr, int key)
 
 	if (screen->exit) {
 		screen->on_exit(screen->cui);
-
-	} else if (handled) {
+	} else if (handled && (screen->cui->current == scr)) {
 		pad_refresh(screen);
 	}
 }
 
+static const char *lang_get_lang_name(struct lang_screen *screen)
+{
+	struct lang *lang;
+	int idx;
+
+	idx = widget_select_get_value(screen->widgets.lang_f);
+
+	/* Option -1 ("Unknown") can only be populated from the current
+	 * language, so there's no change here */
+	if (idx == -1)
+		return NULL;
+
+	lang = &languages[idx];
+
+	return lang->name;
+}
+
+static int lang_process_form(struct lang_screen *screen)
+{
+	struct config *config;
+	const char *lang;
+	int rc;
+
+	config = config_copy(screen, screen->cui->config);
+
+	lang = lang_get_lang_name(screen);
+
+	if (!lang || (config->lang && !strcmp(lang, config->lang)))
+		return 0;
+
+	config->lang = talloc_strdup(screen, lang);
+
+	config->safe_mode = false;
+	rc = cui_send_config(screen->cui, config);
+	talloc_free(config);
+
+	if (rc)
+		pb_log("cui_send_config failed!\n");
+	else
+		pb_debug("config sent!\n");
+
+	return 0;
+}
+
 static void lang_screen_resize(struct nc_scr *scr)
 {
 	struct lang_screen *screen = lang_screen_from_scr(scr);
@@ -130,6 +177,10 @@  static void lang_screen_resize(struct nc_scr *scr)
 static int lang_screen_post(struct nc_scr *scr)
 {
 	struct lang_screen *screen = lang_screen_from_scr(scr);
+
+	if (screen->exit)
+		screen->on_exit(screen->cui);
+
 	widgetset_post(screen->widgetset);
 	nc_scr_frame_draw(scr);
 	wrefresh(screen->scr.main_ncw);
@@ -149,49 +200,39 @@  struct nc_scr *lang_screen_scr(struct lang_screen *screen)
 	return &screen->scr;
 }
 
-static int lang_process_form(struct lang_screen *screen)
+static void lang_screen_update_cb(struct nc_scr *scr)
 {
-	struct config *config;
-	struct lang *lang;
-	int idx, rc;
-
-	config = config_copy(screen, screen->cui->config);
-
-	idx = widget_select_get_value(screen->widgets.lang_f);
-
-	/* Option -1 ("Unknown") can only be populated from the current
-	 * language, so there's no change here */
-	if (idx == -1)
-		return 0;
-
-	lang = &languages[idx];
-
-	if (config->lang && !strcmp(lang->name, config->lang))
-		return 0;
-
-	config->lang = talloc_strdup(screen, lang->name);
-
-	config->safe_mode = false;
-	rc = cui_send_config(screen->cui, config);
-	talloc_free(config);
-
-	if (rc)
-		pb_log("cui_send_config failed!\n");
-	else
-		pb_debug("config sent!\n");
+	struct lang_screen *screen = lang_screen_from_scr(scr);
 
-	return 0;
+	if (!lang_process_form(screen))
+		screen->exit = true;
 }
 
 static void ok_click(void *arg)
 {
 	struct lang_screen *screen = arg;
-	if (lang_process_form(screen))
-		/* errors are written to the status line, so we'll need
-		 * to refresh */
-		wrefresh(screen->scr.main_ncw);
-	else
+	const char *lang;
+
+	if (!widget_checkbox_get_value(screen->widgets.save_cb)) {
+		/* Just update the client display */
+		lang = lang_get_lang_name(screen);
+		if (lang)
+			cui_update_language(screen->cui, lang);
 		screen->exit = true;
+		return;
+	}
+
+	if (discover_client_authenticated(screen->cui->client)) {
+		if (lang_process_form(screen))
+			/* errors are written to the status line, so we'll need
+			 * to refresh */
+			wrefresh(screen->scr.main_ncw);
+		else
+			screen->exit = true;
+	} else {
+		cui_show_auth(screen->cui, screen->scr.main_ncw, false,
+				lang_screen_update_cb);
+	}
 }
 
 static void cancel_click(void *arg)
@@ -221,6 +262,10 @@  static void lang_screen_layout_widgets(struct lang_screen *screen)
 
 	y += 1;
 
+	y += layout_pair(screen, y, screen->widgets.save_l,
+			widget_checkbox_base(screen->widgets.save_cb));
+	y += 1;
+
 	if (screen->cui->config->safe_mode) {
 		widget_move(widget_label_base(screen->widgets.safe_mode),
 			y, screen->field_x);
@@ -289,6 +334,10 @@  static void lang_screen_setup_widgets(struct lang_screen *screen,
 					label, true);
 	}
 
+	screen->widgets.save_l = widget_new_label(set, 0, 0,
+			_("Save changes?"));
+	screen->widgets.save_cb = widget_new_checkbox(set, 0, 0, false);
+
 	if (config->safe_mode)
 		screen->widgets.safe_mode = widget_new_label(set, 0, 0,
 			 _("Selecting 'OK' will exit safe mode"));
@@ -325,7 +374,7 @@  static void lang_screen_draw(struct lang_screen *screen,
 	bool repost = false;
 	int height;
 
-	height = ARRAY_SIZE(languages) + 4;
+	height = ARRAY_SIZE(languages) + N_FIELDS + 4;
 	if (!screen->pad || getmaxy(screen->pad) < height) {
 		if (screen->pad)
 			delwin(screen->pad);
diff --git a/ui/ncurses/nc-plugin.c b/ui/ncurses/nc-plugin.c
index ad8210f0..f897cc8e 100644
--- a/ui/ncurses/nc-plugin.c
+++ b/ui/ncurses/nc-plugin.c
@@ -40,6 +40,8 @@ 
 
 extern const struct help_text plugin_help_text;
 
+static void plugin_run_command(void *arg);
+
 struct plugin_screen {
 	struct nc_scr		scr;
 	struct cui		*cui;
@@ -48,6 +50,7 @@  struct plugin_screen {
 
 	bool			exit;
 	bool			show_help;
+	bool			show_auth_run;
 	bool			need_redraw;
 	void			(*on_exit)(struct cui *);
 
@@ -160,7 +163,7 @@  static void plugin_screen_process_key(struct nc_scr *scr, int key)
 		cui_show_help(screen->cui, _("Petitboot Plugin"),
 				&plugin_help_text);
 
-	} else if (handled) {
+	} else if (handled && (screen->cui->current == scr)) {
 		pad_refresh(screen);
 	}
 }
@@ -178,6 +181,12 @@  static int plugin_screen_post(struct nc_scr *scr)
 	}
 	wrefresh(screen->scr.main_ncw);
 	pad_refresh(screen);
+
+	if (screen->show_auth_run) {
+		screen->show_auth_run = false;
+		plugin_run_command(screen);
+	}
+
 	return 0;
 }
 
@@ -232,28 +241,21 @@  static void plugin_run_command(void *arg)
 	talloc_free(cmd);
 }
 
-/* Call pb-plugin to install a plugin specified by plugin_file */
-int plugin_install_plugin(struct pmenu_item *item)
+static void plugin_run_command_check(void *arg)
 {
-	struct cui *cui = cui_from_item(item);
-	struct cui_opt_data *cod = cod_from_item(item);
-	int rc;
-
-	assert(cui->current == &cui->plugin_menu->scr);
-
-	nc_scr_status_printf(cui->current, _("Installing plugin %s"),
-			cod->pd->plugin_file);
-
-	rc = cui_send_plugin_install(cui, cod->pd->plugin_file);
+	struct plugin_screen *screen = arg;
 
-	if (rc) {
-		pb_log("cui_send_plugin_install failed!\n");
-		nc_scr_status_printf(cui->current,
-				_("Failed to send install request"));
-	} else
-		pb_debug("cui_send_plugin_install sent!\n");
+	if (discover_client_authenticated(screen->cui->client)) {
+		plugin_run_command(screen);
+		return;
+	}
 
-	return rc;
+	/*
+	 * Don't supply a callback as we want to handle running the command
+	 * from the plugin screen.
+	 */
+	screen->show_auth_run = true;
+	cui_show_auth(screen->cui, screen->scr.main_ncw, false, NULL);
 }
 
 static void plugin_screen_setup_widgets(struct plugin_screen *screen)
@@ -291,7 +293,7 @@  static void plugin_screen_setup_widgets(struct plugin_screen *screen)
 	}
 
 	screen->widgets.run_b = widget_new_button(set, 0, 0, 30,
-			_("Run selected command"), plugin_run_command, screen);
+			_("Run selected command"), plugin_run_command_check, screen);
 }
 
 static int layout_pair(struct plugin_screen *screen, int y,
diff --git a/ui/ncurses/nc-plugin.h b/ui/ncurses/nc-plugin.h
index 6dfd4aef..398b7c72 100644
--- a/ui/ncurses/nc-plugin.h
+++ b/ui/ncurses/nc-plugin.h
@@ -29,6 +29,4 @@  struct plugin_screen *plugin_screen_init(struct cui *cui,
 struct nc_scr *plugin_screen_scr(struct plugin_screen *screen);
 void plugin_screen_update(struct plugin_screen *screen);
 
-int plugin_install_plugin(struct pmenu_item *item);
-
 #endif /* defined _NC_PLUGIN_H */
diff --git a/ui/ncurses/nc-scr.h b/ui/ncurses/nc-scr.h
index 5671a6b7..9f46f99b 100644
--- a/ui/ncurses/nc-scr.h
+++ b/ui/ncurses/nc-scr.h
@@ -50,6 +50,7 @@  enum pb_nc_sig {
 	pb_add_url_screen_sig	= 888,
 	pb_subset_screen_sig	= 101,
 	pb_plugin_screen_sig	= 202,
+	pb_auth_screen_sig	= 303,
 	pb_removed_sig		= -999,
 };
 
diff --git a/ui/ncurses/nc-widgets.c b/ui/ncurses/nc-widgets.c
index 30915a97..afd56d71 100644
--- a/ui/ncurses/nc-widgets.c
+++ b/ui/ncurses/nc-widgets.c
@@ -365,8 +365,8 @@  static int textbox_destructor(void *ptr)
 	return 0;
 }
 
-struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set,
-		int y, int x, int len, char *str)
+struct nc_widget_textbox *widget_new_textbox_hidden(struct nc_widgetset *set,
+		int y, int x, int len, char *str, bool hide_input)
 {
 	struct nc_widget_textbox *textbox;
 	FIELD *f;
@@ -383,6 +383,8 @@  struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set,
 	textbox->widget.unfocussed_attr = A_UNDERLINE;
 
 	field_opts_off(f, O_STATIC | O_WRAP | O_BLANK);
+	if (hide_input)
+		field_opts_off(f, O_PUBLIC);
 	set_field_buffer(f, 0, str);
 	set_field_back(f, textbox->widget.unfocussed_attr);
 	set_field_userptr(f, &textbox->widget);
@@ -393,6 +395,12 @@  struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set,
 	return textbox;
 }
 
+struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set,
+		int y, int x, int len, char *str)
+{
+	return widget_new_textbox_hidden(set, y, x, len, str, false);
+}
+
 void widget_textbox_set_fixed_size(struct nc_widget_textbox *textbox)
 {
 	field_opts_on(textbox->widget.field, O_STATIC);
diff --git a/ui/ncurses/nc-widgets.h b/ui/ncurses/nc-widgets.h
index a946c4f5..ce3b0386 100644
--- a/ui/ncurses/nc-widgets.h
+++ b/ui/ncurses/nc-widgets.h
@@ -16,6 +16,7 @@ 
  */
 
 #ifndef NC_WIDGETS_H
+#define NC_WIDGETS_H
 
 struct nc_widgetset;
 struct nc_widget_label;
@@ -27,6 +28,8 @@  struct nc_widget_label *widget_new_label(struct nc_widgetset *set,
 		int y, int x, char *str);
 struct nc_widget_checkbox *widget_new_checkbox(struct nc_widgetset *set,
 		int y, int x, bool checked);
+struct nc_widget_textbox *widget_new_textbox_hidden(struct nc_widgetset *set,
+		int y, int x, int len, char *str, bool hide_input);
 struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set,
 		int y, int x, int len, char *str);
 struct nc_widget_subset *widget_new_subset(struct nc_widgetset *set,