Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/lock_screen.c
T
AskaEth c3abc3fee4 feat(launcher,lock): 锁屏补充三定案—面板一次性解锁恢复口 / 设置页 PIN 管理 / 电源菜单主动锁强化
- 面板一次性解锁恢复口:/tmp/meow_pin_unlock_once 存在即被锁屏页消费删除并放行一次
  (不校验 PIN、不清 system.pin、不重置失败计数;重启标志自然消失;lck_unlock_once_consume,
  launcher 侧只做检查/消费,panel 端点由主会话另实现)。
- 系统设置「安全 → 锁屏 PIN」页(lock_settings.c,subpage.c 脚手架):状态(未设置/已设置/
  锁定中+剩余)/ 设置(4 位双输确认)/ 修改(需旧 PIN 一次)/ 清除(**免旧 PIN**,仅二次确认弹窗)
  / system.pin_lock_on_wake 开关;数字输入用 SDK 按钮键盘(lck_input_pin_modal)。
- 电源菜单「锁定」主动锁强化:立即熄屏 + 写 /tmp/meow_pin_locked(主动锁标志,重启消失,
  唤醒时无论 pin_lock_on_wake 是否开都必锁屏)+ rm /tmp/meow_pin_unlock_once 作废挂起绕过;
  lck_wake_should_lock() 作为唤醒门取代 lck_lock_required(1)(主菜单 2 处 + App 会话 2 处)。
- 测试:test_lock_screen 扩 51 ok——bypass 存在即消费/不存在不影响/消费后删除;清除免旧 PIN;
  主动锁标志唤醒必锁/清除后回退;全回归 settings_brightness/notif_panel/notif_tf/vol_ramp_jack 无碍;
  交叉 -Wall -Wextra 0 warning。meow_launcherd md5 bdf884756ef8a8353aa8e5dfb6e10a0a。
- SYSTEM_SETTINGS.md 同步锁屏行为段。
2026-09-02 00:57:17 +08:00

390 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* lock_screen.c — MeowOS 锁屏 4 位 PIN(纯逻辑;宿主可单测 lock_screen.h)。
*
* 与 meowpanel.c 的访问级 PIN(PANEL_ENHANCE_PLAN)**同源同格式**:`<salt_hex>:<sha256_hex(salt||pin)>`。
* 内联 SHA-256 取自 meow_verify/sha256.c(FIPS 180-4,公有领域参考写法),前缀 lck_ 避免符号冲突;
* 算法与 meowpanel.c 的 mp_sha256 逐字一致 → 面板生成的 panel.pin 无需重设即可被本模块校验。
*
* 日志用 fprintf(stderr)(前缀 [lock]),不依赖 launcher 的 logmsg —— 宿主测试可直接 #include 本文件。
* 依赖 meow_settings_* API(同一 settings.conf),持久化走 meow_settings_save()(含 boot.default
* 同步,幂等)。
*/
#include "lock_screen.h"
#include "meow_settings.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#define LCK_LOG(...) fprintf(stderr, "[lock] " __VA_ARGS__)
/* ---- 最小 SHA-256(FIPS 180-4,公有领域参考写法;内联拷贝自 meow_verify/sha256.c,前缀 lck_) ---- */
static const uint32_t lck_sha256_k[64] = {
0x428a2f98u,0x71374491u,0xb5c0fbcfu,0xe9b5dba5u,0x3956c25bu,0x59f111f1u,0x923f82a4u,0xab1c5ed5u,
0xd807aa98u,0x12835b01u,0x243185beu,0x550c7dc3u,0x72be5d74u,0x80deb1feu,0x9bdc06a7u,0xc19bf174u,
0xe49b69c1u,0xefbe4786u,0x0fc19dc6u,0x240ca1ccu,0x2de92c6fu,0x4a7484aau,0x5cb0a9dcu,0x76f988dau,
0x983e5152u,0xa831c66du,0xb00327c8u,0xbf597fc7u,0xc6e00bf3u,0xd5a79147u,0x06ca6351u,0x14292967u,
0x27b70a85u,0x2e1b2138u,0x4d2c6dfcu,0x53380d13u,0x650a7354u,0x766a0abbu,0x81c2c92eu,0x92722c85u,
0xa2bfe8a1u,0xa81a664bu,0xc24b8b70u,0xc76c51a3u,0xd192e819u,0xd6990624u,0xf40e3585u,0x106aa070u,
0x19a4c116u,0x1e376c08u,0x2748774cu,0x34b0bcb5u,0x391c0cb3u,0x4ed8aa4au,0x5b9cca4fu,0x682e6ff3u,
0x748f82eeu,0x78a5636fu,0x84c87814u,0x8cc70208u,0x90befffau,0xa4506cebu,0xbef9a3f7u,0xc67178f2u
};
#define LCK_ROR(x,n) (((x) >> (n)) | ((x) << (32 - (n))))
#define LCK_CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
#define LCK_MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define LCK_BSIG0(x) (LCK_ROR(x,2) ^ LCK_ROR(x,13) ^ LCK_ROR(x,22))
#define LCK_BSIG1(x) (LCK_ROR(x,6) ^ LCK_ROR(x,11) ^ LCK_ROR(x,25))
#define LCK_SSIG0(x) (LCK_ROR(x,7) ^ LCK_ROR(x,18) ^ ((x) >> 3))
#define LCK_SSIG1(x) (LCK_ROR(x,17) ^ LCK_ROR(x,19) ^ ((x) >> 10))
typedef struct lck_sha256_ctx {
uint32_t state[8];
uint64_t count;
uint8_t buf[64];
size_t buflen;
} lck_sha256_ctx;
static void lck_sha256_init(lck_sha256_ctx *c)
{
c->state[0] = 0x6a09e667u; c->state[1] = 0xbb67ae85u;
c->state[2] = 0x3c6ef372u; c->state[3] = 0xa54ff53au;
c->state[4] = 0x510e527fu; c->state[5] = 0x9b05688cu;
c->state[6] = 0x1f83d9abu; c->state[7] = 0x5be0cd19u;
c->count = 0; c->buflen = 0;
}
static void lck_sha256_block(lck_sha256_ctx *c, const uint8_t *p)
{
uint32_t w[64];
int i;
for (i = 0; i < 16; i++)
w[i] = ((uint32_t)p[i*4] << 24) | ((uint32_t)p[i*4+1] << 16) |
((uint32_t)p[i*4+2] << 8) | (uint32_t)p[i*4+3];
for (i = 16; i < 64; i++)
w[i] = LCK_SSIG1(w[i-2]) + w[i-7] + LCK_SSIG0(w[i-15]) + w[i-16];
uint32_t a = c->state[0], b = c->state[1], cc = c->state[2], d = c->state[3];
uint32_t e = c->state[4], f = c->state[5], g = c->state[6], h = c->state[7];
for (i = 0; i < 64; i++) {
uint32_t t1 = h + LCK_BSIG1(e) + LCK_CH(e,f,g) + lck_sha256_k[i] + w[i];
uint32_t t2 = LCK_BSIG0(a) + LCK_MAJ(a,b,cc);
h = g; g = f; f = e; e = d + t1;
d = cc; cc = b; b = a; a = t1 + t2;
}
c->state[0] += a; c->state[1] += b; c->state[2] += cc; c->state[3] += d;
c->state[4] += e; c->state[5] += f; c->state[6] += g; c->state[7] += h;
}
static void lck_sha256_update(lck_sha256_ctx *c, const uint8_t *data, size_t len)
{
c->count += (uint64_t)len;
if (c->buflen) {
size_t need = 64 - c->buflen;
size_t take = len < need ? len : need;
memcpy(c->buf + c->buflen, data, take);
c->buflen += take; data += take; len -= take;
if (c->buflen == 64) { lck_sha256_block(c, c->buf); c->buflen = 0; }
}
while (len >= 64) { lck_sha256_block(c, data); data += 64; len -= 64; }
if (len) { memcpy(c->buf, data, len); c->buflen = len; }
}
static void lck_sha256_final(lck_sha256_ctx *c, uint8_t out[32])
{
uint64_t bitlen = c->count * 8;
uint8_t *b = c->buf;
size_t bl = c->buflen;
int i;
b[bl++] = 0x80;
if (bl > 56) {
while (bl < 64) b[bl++] = 0;
lck_sha256_block(c, b);
bl = 0;
}
while (bl < 56) b[bl++] = 0;
for (i = 0; i < 8; i++)
b[bl++] = (uint8_t)(bitlen >> (56 - i * 8));
lck_sha256_block(c, b);
for (i = 0; i < 8; i++) {
out[i*4] = (uint8_t)(c->state[i] >> 24);
out[i*4+1] = (uint8_t)(c->state[i] >> 16);
out[i*4+2] = (uint8_t)(c->state[i] >> 8);
out[i*4+3] = (uint8_t)(c->state[i]);
}
}
#undef LCK_ROR
#undef LCK_CH
#undef LCK_MAJ
#undef LCK_BSIG0
#undef LCK_BSIG1
#undef LCK_SSIG0
#undef LCK_SSIG1
/* ---- 哈希基元(格式 `<salt_hex>:<sha256_hex(salt||pin)>`,与面板全等)---- */
static void lck_hex(const unsigned char *raw, size_t n, char *out)
{
static const char hex[] = "0123456789abcdef";
for (size_t i = 0; i < n; i++) {
out[i*2] = hex[raw[i] >> 4];
out[i*2+1] = hex[raw[i] & 0xf];
}
out[n*2] = 0;
}
static int lck_read_random(unsigned char *buf, size_t n)
{
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) return -1;
size_t got = 0;
while (got < n) {
ssize_t rd = read(fd, buf + got, n - got);
if (rd <= 0) { close(fd); return -1; }
got += (size_t)rd;
}
close(fd);
return 0;
}
static int lck_make_salt(char *out, int outcap) /* 16B → 32 hex 盐 */
{
unsigned char raw[16];
if (outcap < 33) return -1;
if (lck_read_random(raw, sizeof raw) < 0) return -1;
lck_hex(raw, sizeof raw, out);
return 0;
}
static int lck_pass_hash(const char *salt, const char *password, char *out, int outcap)
{
lck_sha256_ctx c;
uint8_t digest[32];
if (outcap < LCK_HASH_LEN + 1) return -1;
lck_sha256_init(&c);
lck_sha256_update(&c, (const uint8_t *)salt, strlen(salt));
lck_sha256_update(&c, (const uint8_t *)password, strlen(password));
lck_sha256_final(&c, digest);
lck_hex(digest, sizeof digest, out);
return 0;
}
static int lck_make_hash(const char *password, char *out, int outcap) /* salt:hex */
{
char salt[33], h[LCK_HASH_LEN + 1];
if (lck_make_salt(salt, sizeof salt) < 0) return -1;
if (lck_pass_hash(salt, password, h, sizeof h) < 0) return -1;
snprintf(out, (size_t)outcap, "%s:%s", salt, h);
return 0;
}
/* 校验 `salt:hex` 是否匹配明文(恒定比较,不反解)。stored 为 `salt:hex` 完整串。 */
static int lck_hash_matches(const char *stored, const char *password)
{
if (!stored || !stored[0] || !password) return 0;
char tmp[160];
snprintf(tmp, sizeof tmp, "%s", stored);
char *colon = strchr(tmp, ':');
if (!colon) return 0;
*colon = 0;
char want[LCK_HASH_LEN + 1];
if (lck_pass_hash(tmp, password, want, sizeof want) != 0) return 0;
if (strlen(colon + 1) != LCK_HASH_LEN) return 0;
return memcmp(want, colon + 1, LCK_HASH_LEN) == 0;
}
/* ---- 设置读取辅助(NULL 表示键缺失/空)---- */
static char *lck_get(const char *key)
{
char buf[MEOW_SET_MAX_LEN];
if (meow_settings_get_str(key, buf, sizeof buf) != 0) return NULL;
if (!buf[0]) return NULL; /* 空值=未设 */
char *r = strdup(buf);
return r;
}
static int lck_get_int(const char *key)
{
int v = 0;
if (meow_settings_get_int(key, &v) != 0) v = 0;
return v;
}
static int lck_set_int(const char *key, int v)
{
return meow_settings_set_int(key, v);
}
static int lck_persist(void)
{
return meow_settings_save();
}
/* ---- 公开接口 ---- */
int lck_pin_valid(const char *pin)
{
if (!pin) return 0;
size_t l = strlen(pin);
if (l != LCK_PIN_LEN) return 0;
for (size_t i = 0; i < l; i++)
if (pin[i] < '0' || pin[i] > '9') return 0;
return 1;
}
int lck_pin_configured(void)
{
char *sp = lck_get(LCK_KEY_PIN);
int s = (sp != NULL);
free(sp);
if (s) return 1;
char *pp = lck_get(LCK_KEY_PANEL_PIN);
int p = (pp != NULL);
free(pp);
return p;
}
int lck_lock_required(int allow_disable)
{
if (!lck_pin_configured()) return 0; /* 未设凭据:锁屏无鉴别物,不开 */
if (allow_disable) {
int wake = 1;
if (meow_settings_get_int(LCK_KEY_WAKE, &wake) != 0) wake = 1;
return wake ? 1 : 0;
}
return 1; /* 手动锁:不看 WAKE 键 */
}
int lck_pin_check(const char *pin)
{
if (!lck_pin_valid(pin)) return 0;
char *sp = lck_get(LCK_KEY_PIN);
if (sp) {
int ok = lck_hash_matches(sp, pin);
free(sp);
return ok;
}
/* system.pin 未设:迁移期回退校验 panel.pin */
char *pp = lck_get(LCK_KEY_PANEL_PIN);
if (pp) {
int ok = lck_hash_matches(pp, pin);
free(pp);
return ok;
}
return 0;
}
int lck_pin_set(const char *pin)
{
if (!lck_pin_valid(pin)) return MEOW_ERR_INVAL;
char hash[160]; /* 32(salt) + ':' + 64(sha256 hex) + NUL = 98 */
if (lck_make_hash(pin, hash, sizeof hash) != 0) return MEOW_ERR_IO;
if (meow_settings_set_str(LCK_KEY_PIN, hash) != MEOW_OK) return MEOW_ERR_INVAL;
lck_set_int(LCK_KEY_FAILS, 0);
lck_set_int(LCK_KEY_LOCK_UNTIL, 0);
lck_persist();
LCK_LOG("pin set (%d digits, hash stored salt:sha256)\n", LCK_PIN_LEN);
return MEOW_OK;
}
int lck_pin_clear(void)
{
if (meow_settings_set_str(LCK_KEY_PIN, "") != MEOW_OK) return MEOW_ERR_INVAL;
lck_set_int(LCK_KEY_FAILS, 0);
lck_set_int(LCK_KEY_LOCK_UNTIL, 0);
lck_persist();
LCK_LOG("pin cleared (unlock)\n");
return MEOW_OK;
}
int lck_pin_success(void)
{
char *sp = lck_get(LCK_KEY_PIN);
if (!sp) {
/* 首次成功:把 panel.pin 并入 system.pin(一次性迁移),此后 system.pin 为准 */
char *pp = lck_get(LCK_KEY_PANEL_PIN);
if (pp) {
meow_settings_set_str(LCK_KEY_PIN, pp);
LCK_LOG("success: migrated panel.pin -> system.pin\n");
free(pp);
} else {
LCK_LOG("success: no system.pin and no panel.pin?!\n");
}
}
free(sp);
lck_set_int(LCK_KEY_FAILS, 0);
lck_set_int(LCK_KEY_LOCK_UNTIL, 0);
lck_persist();
return MEOW_OK;
}
int lck_pin_fail(void)
{
time_t now = time(NULL);
int fails = lck_get_int(LCK_KEY_FAILS);
int until = lck_get_int(LCK_KEY_LOCK_UNTIL);
/* 已锁定:不再递增(锁屏页本应禁输入;防御) */
if (until > 0 && (time_t)until > now)
return (int)((time_t)until - now);
fails++;
if (fails >= LCK_PIN_MAX_FAILS) {
until = (int)now + LCK_PIN_LOCK_SEC;
fails = 0;
LCK_LOG("fail: %d consecutive -> LOCK %ds (until=%d)\n",
LCK_PIN_MAX_FAILS, LCK_PIN_LOCK_SEC, until);
} else {
until = 0;
LCK_LOG("fail: attempt %d/%d\n", fails, LCK_PIN_MAX_FAILS);
}
lck_set_int(LCK_KEY_FAILS, fails);
lck_set_int(LCK_KEY_LOCK_UNTIL, until);
lck_persist();
return (until > 0 && (time_t)until > now) ? (int)((time_t)until - now) : 0;
}
time_t lck_lock_remaining(void)
{
time_t now = time(NULL);
int until = lck_get_int(LCK_KEY_LOCK_UNTIL);
if (until > 0 && (time_t)until > now)
return (time_t)until - now;
if (until > 0) {
/* 截止已过:清锁定戳+计数(进程内解禁),落盘 */
LCK_LOG("lock expired (%d < now=%ld), clearing\n", until, (long)now);
lck_set_int(LCK_KEY_LOCK_UNTIL, 0);
lck_set_int(LCK_KEY_FAILS, 0);
lck_persist();
}
return 0;
}
/* ---- 面板一次性解锁恢复口(tmpfs 标志)---- */
int lck_unlock_once_consume(void)
{
if (access(LCK_UNLOCK_ONCE_FILE, F_OK) != 0) return 0; /* 不存在:不影响 */
unlink(LCK_UNLOCK_ONCE_FILE);
LCK_LOG("unlock-once: bypass file present -> consume + release once\n");
return 1;
}
int lck_manual_lock_set(void)
{
int fd = open(LCK_MANUAL_LOCK_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) { LCK_LOG("manual-lock: open %s fail\n", LCK_MANUAL_LOCK_FILE); return MEOW_ERR_IO; }
write(fd, "1", 1); close(fd);
LCK_LOG("manual-lock: set %s\n", LCK_MANUAL_LOCK_FILE);
return MEOW_OK;
}
void lck_manual_lock_clear(void)
{
if (access(LCK_MANUAL_LOCK_FILE, F_OK) == 0) {
unlink(LCK_MANUAL_LOCK_FILE);
LCK_LOG("manual-lock: cleared\n");
}
}
int lck_wake_should_lock(void)
{
if (!lck_pin_configured()) return 0;
int wake = 1;
if (meow_settings_get_int(LCK_KEY_WAKE, &wake) != 0) wake = 1;
if (wake) return 1;
/* 唤醒锁已关,但存在「主动锁」标志(电源菜单「锁定」)→ 仍必锁屏 */
return access(LCK_MANUAL_LOCK_FILE, F_OK) == 0;
}