Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd895db0c9 | ||
|
|
e4087441a5 | ||
|
|
5a16c4e1bf |
@@ -4,3 +4,26 @@ build/
|
||||
/*.a
|
||||
!resources/
|
||||
!resources/**
|
||||
|
||||
# Exclude nested .git and bare repos inside resources/nostr_core_lib.
|
||||
# These are not needed for the build and add ~1.3 GB to the Docker context.
|
||||
resources/nostr_core_lib/.git/
|
||||
resources/nostr_core_lib/rewrite_mirror/
|
||||
resources/nostr_core_lib/verify_remote_size/
|
||||
resources/nostr_core_lib/verify_remote_size_now/
|
||||
resources/nostr_core_lib/backups/
|
||||
resources/nostr_core_lib/examples/
|
||||
resources/nostr_core_lib/tests/
|
||||
resources/nostr_core_lib/plans/
|
||||
resources/nostr_core_lib/pool.log
|
||||
resources/nostr_core_lib/Trash/
|
||||
resources/nostr_core_lib/node_modules/
|
||||
resources/nostr_core_lib/nips/
|
||||
resources/nostr_core_lib/nak/
|
||||
resources/nostr_core_lib/nostr-tools/
|
||||
resources/nostr_core_lib/libsodium/
|
||||
resources/nostr_core_lib/monocypher-4.0.2/
|
||||
resources/nostr_core_lib/tiny-AES-c/
|
||||
resources/nostr_core_lib/blossom/
|
||||
resources/nostr_core_lib/ndk/
|
||||
resources/nostr_core_lib/cline_history/
|
||||
|
||||
@@ -93,12 +93,38 @@ N_SIGNER_CLIENT_TARGET := $(BUILD_DIR)/nsigner_client
|
||||
|
||||
.PHONY: all lib dev static static-debug static-arm64 firmware-feather test test-integration test-mnemonic test-mnemonic-input test-role test-selector test-enforcement test-dispatcher test-policy test-socket-name test-auth-envelope test-qrexec-auth test-mine-event test-pq-crypto test-ed25519-x25519 test-ml-dsa-65 test-slh-dsa-128s test-ml-kem-768 test-pubkey-format test-algorithm-api test-path-whitelist test-n-signer-client examples clients test-client clean
|
||||
|
||||
# Guard for non-static build targets.
|
||||
# The canonical build is `make static` (runs build_static.sh).
|
||||
# To use dev/test targets, set NSIGNER_ALLOW_DEV_BUILD=1 in your environment.
|
||||
# This prevents AI agents from accidentally using the wrong build path.
|
||||
define BUILD_GUARD
|
||||
@if [ -z "$$NSIGNER_ALLOW_DEV_BUILD" ]; then \
|
||||
echo "=========================================================="; \
|
||||
echo "ERROR: This target is blocked for non-interactive agents."; \
|
||||
echo " For testing and deployment, use:"; \
|
||||
echo ""; \
|
||||
echo " ./build_static.sh"; \
|
||||
echo " or"; \
|
||||
echo " make static"; \
|
||||
echo ""; \
|
||||
echo " The static build produces the canonical binary that"; \
|
||||
echo " matches production deployments."; \
|
||||
echo ""; \
|
||||
echo " To override (human developers only):"; \
|
||||
echo " NSIGNER_ALLOW_DEV_BUILD=1 make <target>"; \
|
||||
echo "=========================================================="; \
|
||||
exit 1; \
|
||||
fi
|
||||
endef
|
||||
|
||||
all: dev clients
|
||||
|
||||
lib:
|
||||
$(BUILD_GUARD)
|
||||
cd resources/nostr_core_lib && ./build.sh --nips=1,4,6,13,19,44
|
||||
|
||||
dev: lib $(TARGET_DEV)
|
||||
$(BUILD_GUARD)
|
||||
|
||||
$(TARGET_DEV): $(SOURCES)
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
|
||||
+75
-7
@@ -1,21 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Build fully static MUSL binary for nsigner using Alpine Docker
|
||||
#
|
||||
# Speed optimization: if nothing changed since the last successful build,
|
||||
# skip the Docker build entirely and reuse the existing binaries.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
BUILD_DIR="$SCRIPT_DIR/build"
|
||||
DOCKERFILE="$SCRIPT_DIR/Dockerfile.alpine-musl"
|
||||
HASH_FILE="$BUILD_DIR/.nsigner_build_hash"
|
||||
|
||||
TARGET_ARCH=""
|
||||
FORCE=0
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--arch)
|
||||
if [[ -z "${2:-}" ]]; then
|
||||
echo "ERROR: --arch requires a value"
|
||||
echo "Usage: $0 [--arch <x86_64|arm64|armv7>]"
|
||||
echo "Usage: $0 [--arch <x86_64|arm64|armv7>] [--force]"
|
||||
exit 1
|
||||
fi
|
||||
case "$2" in
|
||||
@@ -30,9 +35,13 @@ while [[ $# -gt 0 ]]; do
|
||||
esac
|
||||
shift 2
|
||||
;;
|
||||
--force)
|
||||
FORCE=1
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: Unknown argument '$1'"
|
||||
echo "Usage: $0 [--arch <x86_64|arm64|armv7>]"
|
||||
echo "Usage: $0 [--arch <x86_64|arm64|armv7>] [--force]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -78,7 +87,7 @@ case "$ARCH" in
|
||||
CLIENT_NAME="nsigner_client_static_arm64"
|
||||
;;
|
||||
armv7)
|
||||
PLATFORM="linux/arm/v7"
|
||||
PLATFORM="linux/v7"
|
||||
OUTPUT_NAME="nsigner_static_armv7"
|
||||
CLIENT_NAME="nsigner_client_static_armv7"
|
||||
;;
|
||||
@@ -109,6 +118,62 @@ echo "Output: $BUILD_DIR/$OUTPUT_NAME"
|
||||
echo "Client: $BUILD_DIR/$CLIENT_NAME"
|
||||
echo ""
|
||||
|
||||
# ---- Change detection ----
|
||||
# Compute a hash of all files that feed into the Docker build.
|
||||
# If the hash matches the last successful build and the output binaries
|
||||
# exist, skip the Docker build entirely.
|
||||
compute_source_hash() {
|
||||
{
|
||||
# Dockerfile itself
|
||||
cat "$DOCKERFILE"
|
||||
# .dockerignore
|
||||
cat "$SCRIPT_DIR/.dockerignore" 2>/dev/null || true
|
||||
# All source files
|
||||
find "$SCRIPT_DIR/src" "$SCRIPT_DIR/client" "$SCRIPT_DIR/libotppad" \
|
||||
"$SCRIPT_DIR/resources/tui_continuous" "$SCRIPT_DIR/resources/pqclean" \
|
||||
-type f -not -path '*/.git/*' 2>/dev/null | sort | xargs cat 2>/dev/null
|
||||
# nostr_core_lib source (exclude .git, backups, bare repos, examples, tests)
|
||||
find "$SCRIPT_DIR/resources/nostr_core_lib" \
|
||||
-type f \
|
||||
-not -path '*/.git/*' \
|
||||
-not -path '*/rewrite_mirror/*' \
|
||||
-not -path '*/verify_remote_size*' \
|
||||
-not -path '*/backups/*' \
|
||||
-not -path '*/examples/*' \
|
||||
-not -path '*/tests/*' \
|
||||
-not -path '*/Trash/*' \
|
||||
-not -path '*/node_modules/*' \
|
||||
-not -path '*/nips/*' \
|
||||
-not -path '*/nak/*' \
|
||||
-not -path '*/nostr-tools/*' \
|
||||
-not -path '*/libsodium/*' \
|
||||
-not -path '*/monocypher*' \
|
||||
-not -path '*/tiny-AES-c/*' \
|
||||
-not -path '*/blossom/*' \
|
||||
-not -path '*/ndk/*' \
|
||||
-not -path '*/cline_history/*' \
|
||||
2>/dev/null | sort | xargs cat 2>/dev/null
|
||||
} | sha256sum | awk '{print $1}'
|
||||
}
|
||||
|
||||
CURRENT_HASH="$(compute_source_hash)"
|
||||
OUTPUT_PATH="$BUILD_DIR/$OUTPUT_NAME"
|
||||
CLIENT_PATH="$BUILD_DIR/$CLIENT_NAME"
|
||||
|
||||
if [[ "$FORCE" -eq 0 ]] && \
|
||||
[[ -f "$OUTPUT_PATH" ]] && \
|
||||
[[ -f "$CLIENT_PATH" ]] && \
|
||||
[[ -f "$HASH_FILE" ]] && \
|
||||
[[ "$(cat "$HASH_FILE" 2>/dev/null)" == "$CURRENT_HASH" ]]; then
|
||||
echo "No changes detected since last successful build."
|
||||
echo "Skipping Docker build. Existing binaries:"
|
||||
echo " $OUTPUT_PATH"
|
||||
echo " $CLIENT_PATH"
|
||||
echo ""
|
||||
echo "Use --force to rebuild anyway."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$ARCH" != "$HOST_ARCH" ]; then
|
||||
echo "[0/3] Preparing buildx + QEMU for cross-architecture build"
|
||||
if ! docker buildx inspect >/dev/null 2>&1; then
|
||||
@@ -126,10 +191,10 @@ if [ "$ARCH" != "$HOST_ARCH" ]; then
|
||||
fi
|
||||
|
||||
echo "[1/3] Building builder stage from project root context"
|
||||
# Remove previous builder image to avoid dangling <none> images piling up
|
||||
# across repeated builds (each rebuild untagges the old image, leaving ~422MB
|
||||
# of garbage per build otherwise).
|
||||
docker rmi "$IMAGE_TAG" >/dev/null 2>&1 || true
|
||||
# Note: we no longer docker rmi before building. The buildx cache handles
|
||||
# layer reuse, and the prune at the end prevents dangling images. Removing
|
||||
# the image here forced a full --load re-export (~370MB) every time even
|
||||
# when all layers were cache hits.
|
||||
docker buildx build \
|
||||
--platform "$PLATFORM" \
|
||||
--target builder \
|
||||
@@ -173,6 +238,9 @@ echo "Build complete:"
|
||||
echo " $BUILD_DIR/$OUTPUT_NAME"
|
||||
echo " $BUILD_DIR/$CLIENT_NAME"
|
||||
|
||||
# Record the source hash so the next run can skip if nothing changed.
|
||||
echo "$CURRENT_HASH" > "$HASH_FILE"
|
||||
|
||||
# Prune stale build cache older than 24h to prevent unbounded cache growth
|
||||
# from repeated buildx builds. Recent layers are kept for fast rebuilds.
|
||||
docker builder prune -af --filter "until=24h" >/dev/null 2>&1 || true
|
||||
|
||||
@@ -110,8 +110,11 @@ static void print_usage(FILE *fp, const char *prog) {
|
||||
" %s --algorithm ed25519 --index 0 sign 68656c6c6f\n"
|
||||
"\n"
|
||||
" # Get signer metadata\n"
|
||||
" %s get-info\n",
|
||||
prog, prog, prog, prog, prog, prog, prog, prog, prog, prog, prog);
|
||||
" %s get-info\n"
|
||||
"\n"
|
||||
" # Qubes qrexec: get the first pubkey from a signer in the nostr_signer qube\n"
|
||||
" %s --qrexec nostr_signer:qubes.NsignerRpc --role nostr_range --path \"m/44'/1237'/0'/0/0\" get-public-key\n",
|
||||
prog, prog, prog, prog, prog, prog, prog, prog, prog, prog, prog, prog);
|
||||
}
|
||||
|
||||
/* Read one line from stdin (newline stripped). Returns malloc'd string or NULL on EOF/error. */
|
||||
|
||||
+2
-26
@@ -4,9 +4,8 @@
|
||||
# and install them to /usr/local/bin/
|
||||
#
|
||||
# Usage:
|
||||
# ./deploy_local.sh # build + install (prompts for sudo)
|
||||
# ./deploy_local.sh # build + install (uses sudo if needed)
|
||||
# ./deploy_local.sh --no-build # install existing build/ binaries only
|
||||
# ./deploy_local.sh --force # skip confirmation prompt
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
@@ -41,7 +40,6 @@ case "$ARCH" in
|
||||
esac
|
||||
|
||||
DO_BUILD=true
|
||||
FORCE=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
@@ -49,10 +47,6 @@ while [[ $# -gt 0 ]]; do
|
||||
DO_BUILD=false
|
||||
shift
|
||||
;;
|
||||
--force|-f)
|
||||
FORCE=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
echo "deploy_local.sh — Build and install nsigner + nsigner_client to $INSTALL_PREFIX"
|
||||
echo ""
|
||||
@@ -60,13 +54,12 @@ while [[ $# -gt 0 ]]; do
|
||||
echo ""
|
||||
echo "OPTIONS:"
|
||||
echo " --no-build Skip build step; install existing binaries from build/"
|
||||
echo " --force, -f Skip confirmation prompt"
|
||||
echo " -h, --help Show this help message"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: Unknown argument '$1'"
|
||||
echo "Usage: $0 [--no-build] [--force]"
|
||||
echo "Usage: $0 [--no-build]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -119,23 +112,6 @@ echo " OK: $CLIENT_BIN ($(du -h "$CLIENT_BIN" | cut -f1))"
|
||||
SIGNER_VERSION="$("$SIGNER_BIN" --version 2>&1 || echo "unknown")"
|
||||
echo " Signer version: $SIGNER_VERSION"
|
||||
|
||||
# --- Confirm ------------------------------------------------------------------
|
||||
if ! $FORCE; then
|
||||
echo ""
|
||||
echo "About to install:"
|
||||
echo " $SIGNER_BIN -> $INSTALL_PREFIX/nsigner"
|
||||
echo " $CLIENT_BIN -> $INSTALL_PREFIX/nsigner_client"
|
||||
echo ""
|
||||
read -r -p "Proceed? [y/N] " response
|
||||
case "$response" in
|
||||
[yY][eE][sS]|[yY]) ;;
|
||||
*)
|
||||
echo "Aborted."
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# --- Install ------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "[3/3] Installing to $INSTALL_PREFIX"
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
# Plan: Role-as-Password as the Default Authorization Model
|
||||
|
||||
## Status: Draft — ready for review
|
||||
|
||||
## Problem
|
||||
|
||||
The signer currently defaults to requiring interactive approval for every request, even for the default "main" role. This contradicts the intended design where **knowing the role name is sufficient authorization** (role-as-password). Users running v0.1.21 are prompted to approve requests when they should be authorized automatically.
|
||||
|
||||
## Root cause
|
||||
|
||||
Three places in the code set `requires_approval = 1` by default:
|
||||
|
||||
| Location | Context | Current value |
|
||||
|---|---|---|
|
||||
| [`src/main.c:3816`](../src/main.c:3816) | Default "main" role (non-interactive mode) | `requires_approval = 1` |
|
||||
| [`src/main.c:2374`](../src/main.c:2374) | Wizard role creation prompt | Default `1` (Y/n) |
|
||||
| [`src/main.c:2223`](../src/main.c:2223) | Wizard OTP role creation prompt | Default `1` (Y/n) |
|
||||
|
||||
## Changes required
|
||||
|
||||
### 1. Default "main" role — non-interactive mode
|
||||
|
||||
**File:** [`src/main.c:3816`](../src/main.c:3816)
|
||||
|
||||
Change:
|
||||
```c
|
||||
role.requires_approval = 1;
|
||||
```
|
||||
To:
|
||||
```c
|
||||
role.requires_approval = 0; /* role-as-password: knowing the role name is sufficient */
|
||||
```
|
||||
|
||||
This is the most critical fix — it affects every user running in non-interactive mode (e.g., `--listen unix`, `--listen tcp`, `--listen qrexec`).
|
||||
|
||||
### 2. Wizard role creation — remove approval prompt
|
||||
|
||||
**File:** [`src/main.c:2359-2377`](../src/main.c:2359)
|
||||
|
||||
Currently the wizard asks:
|
||||
```
|
||||
Require interactive approval for each request? [Y/n]:
|
||||
```
|
||||
|
||||
With role-as-password as the default, this prompt should be **removed entirely**. The role is created with `requires_approval = 0`. If a user wants approval, they can use `--preapprove` or manually edit the role after creation.
|
||||
|
||||
Remove the prompt block (lines 2359-2377) and set:
|
||||
```c
|
||||
int requires_approval = 0; /* role-as-password by default */
|
||||
```
|
||||
|
||||
### 3. Wizard OTP role creation — remove approval prompt
|
||||
|
||||
**File:** [`src/main.c:2208-2226`](../src/main.c:2208)
|
||||
|
||||
Same change as #2. Remove the approval prompt for OTP roles and default to `requires_approval = 0`.
|
||||
|
||||
### 4. Policy table default — no change needed
|
||||
|
||||
**File:** [`src/policy.c:1097-1113`](../src/policy.c:1097)
|
||||
|
||||
The current default policy is `* → PROMPT_EVERY_REQUEST`. This is correct because:
|
||||
|
||||
- For roles with `requires_approval = 0`, [`policy_check_with_role()`](../src/policy.c:1305) returns `POLICY_ALLOW` **before** consulting the policy table.
|
||||
- For roles with `requires_approval = 1`, the policy table prompt still fires as expected.
|
||||
|
||||
No change needed here.
|
||||
|
||||
### 5. `--allow-all` flag — retain as-is
|
||||
|
||||
**File:** [`src/main.c:3678`](../src/main.c:3678)
|
||||
|
||||
The `--allow-all` flag sets `g_prompt_always_allow`, which bypasses prompts for algorithm-based verbs (sign, verify, encapsulate, etc.) that don't go through the role system. This is still useful for testing and non-interactive scenarios. No change needed.
|
||||
|
||||
## Authorization flow after changes
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Client sends request with role name] --> B{Role found?}
|
||||
B -- No --> C[Reject: unknown_role]
|
||||
B -- Yes --> D{requires_approval?}
|
||||
D -- No --> E[Authorize immediately - role-as-password]
|
||||
D -- Yes --> F[Check policy table]
|
||||
F --> G{Policy match?}
|
||||
G -- Allow --> H[Authorize]
|
||||
G -- Prompt --> I[Show interactive prompt]
|
||||
G -- Deny --> J[Reject: policy_denied]
|
||||
I --> K{User choice}
|
||||
K -- y --> H
|
||||
K -- a/e --> L[Add session grant] --> H
|
||||
K -- n --> J
|
||||
```
|
||||
|
||||
## Test impact
|
||||
|
||||
- [`tests/test_n_signer_client.sh`](../tests/test_n_signer_client.sh) — may need updates if tests relied on the old approval-required default
|
||||
- [`tests/test_integration.c`](../tests/test_integration.c) — verify no tests break from the default change
|
||||
- The `NSIGNER_TEST_FORCE_PROMPT` env var ([`src/main.c:3063`](../src/main.c:3063)) can be used to force prompts in tests that need to exercise the approval path
|
||||
|
||||
## Files to modify
|
||||
|
||||
| File | Lines | Change |
|
||||
|---|---|---|
|
||||
| [`src/main.c`](../src/main.c) | 3816 | `requires_approval = 1` → `0` |
|
||||
| [`src/main.c`](../src/main.c) | 2359-2377 | Remove approval prompt, default to `0` |
|
||||
| [`src/main.c`](../src/main.c) | 2208-2226 | Remove approval prompt, default to `0` |
|
||||
|
||||
## Summary
|
||||
|
||||
Three one-line changes (plus removing two prompt blocks) to make role-as-password the default. The mechanism already exists in the code — it's just not the default.
|
||||
+18
-44
@@ -813,8 +813,8 @@ int socket_name_random(char *out, size_t out_len);
|
||||
/* Version information (auto-updated by build/version tooling) */
|
||||
#define NSIGNER_VERSION_MAJOR 0
|
||||
#define NSIGNER_VERSION_MINOR 1
|
||||
#define NSIGNER_VERSION_PATCH 20
|
||||
#define NSIGNER_VERSION "v0.1.20"
|
||||
#define NSIGNER_VERSION_PATCH 23
|
||||
#define NSIGNER_VERSION "v0.1.23"
|
||||
|
||||
|
||||
/* NSIGNER_HEADERLESS_DECLS_END */
|
||||
@@ -2206,24 +2206,9 @@ static int prompt_named_path_roles(role_table_t *role_table) {
|
||||
strncpy(g_wizard_otp_spec, pad_spec, sizeof(g_wizard_otp_spec) - 1);
|
||||
g_wizard_otp_spec[sizeof(g_wizard_otp_spec) - 1] = '\0';
|
||||
|
||||
/* requires_approval flag — OTP roles can still require approval. */
|
||||
printf(" Require interactive approval for each request? [Y/n]: ");
|
||||
fflush(stdout);
|
||||
char approval_choice[16];
|
||||
if (read_line_stdin(approval_choice, sizeof(approval_choice)) != 0) {
|
||||
return (roles_created > 0) ? 0 : -1;
|
||||
}
|
||||
{
|
||||
size_t len = strlen(approval_choice);
|
||||
while (len > 0 && (approval_choice[len-1] == '\n' || approval_choice[len-1] == '\r' ||
|
||||
approval_choice[len-1] == ' ' || approval_choice[len-1] == '\t')) {
|
||||
approval_choice[--len] = '\0';
|
||||
}
|
||||
}
|
||||
int requires_approval = 1;
|
||||
if (approval_choice[0] == 'n' || approval_choice[0] == 'N') {
|
||||
requires_approval = 0;
|
||||
}
|
||||
/* Role-as-password by default: knowing the role name is sufficient
|
||||
* authorization. No interactive approval prompt. */
|
||||
int requires_approval = 0;
|
||||
|
||||
/* Register the OTP role. No curve/path derivation; the pad is the key. */
|
||||
role_entry_t new_role;
|
||||
@@ -2357,24 +2342,9 @@ static int prompt_named_path_roles(role_table_t *role_table) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* requires_approval flag */
|
||||
printf(" Require interactive approval for each request? [Y/n]: ");
|
||||
fflush(stdout);
|
||||
char approval_choice[16];
|
||||
if (read_line_stdin(approval_choice, sizeof(approval_choice)) != 0) {
|
||||
return (roles_created > 0) ? 0 : -1;
|
||||
}
|
||||
{
|
||||
size_t len = strlen(approval_choice);
|
||||
while (len > 0 && (approval_choice[len-1] == '\n' || approval_choice[len-1] == '\r' ||
|
||||
approval_choice[len-1] == ' ' || approval_choice[len-1] == '\t')) {
|
||||
approval_choice[--len] = '\0';
|
||||
}
|
||||
}
|
||||
int requires_approval = 1; /* default: require approval */
|
||||
if (approval_choice[0] == 'n' || approval_choice[0] == 'N') {
|
||||
requires_approval = 0;
|
||||
}
|
||||
/* Role-as-password by default: knowing the role name is sufficient
|
||||
* authorization. No interactive approval prompt. */
|
||||
int requires_approval = 0;
|
||||
|
||||
/* No default index — the client always sends the full path, so keys
|
||||
* are derived on-demand when a request comes in. */
|
||||
@@ -3813,7 +3783,7 @@ int main(int argc, char *argv[]) {
|
||||
role.path_range_lo = -1;
|
||||
role.path_range_hi = -1;
|
||||
role.path_default_index = -1;
|
||||
role.requires_approval = 1;
|
||||
role.requires_approval = 0; /* role-as-password: knowing the role name is sufficient */
|
||||
role.derived = 0;
|
||||
if (role_table_add(&role_table, &role) != 0) {
|
||||
fprintf(stderr, "Failed to initialize default role\n");
|
||||
@@ -4345,12 +4315,16 @@ int main(int argc, char *argv[]) {
|
||||
} else if (lower == 'd') {
|
||||
/* Display connection instructions on demand.
|
||||
* Show the connections screen, wait for any key, then
|
||||
* return to the normal status display. */
|
||||
* return to the normal status display.
|
||||
*
|
||||
* Use tui_get_key() rather than a bare read() so the
|
||||
* wait survives EINTR (e.g. SIGWINCH); a bare read()
|
||||
* returns immediately on signal interruption, which
|
||||
* causes render_status() to run right away and scroll
|
||||
* the connections view off the screen before the user
|
||||
* has a chance to read it. */
|
||||
render_connections(&role_table, &mnemonic, derived_count, socket_name);
|
||||
{
|
||||
char dch = '\0';
|
||||
(void)read(STDIN_FILENO, &dch, 1);
|
||||
}
|
||||
(void)tui_get_key();
|
||||
render_status(&role_table, &mnemonic, derived_count, socket_name);
|
||||
} else if (lower == 'l') {
|
||||
mnemonic_source_t relock_source;
|
||||
|
||||
Reference in New Issue
Block a user