/*
 * auth.c  —  認証デーモン (problem5 / Pwn)
 *
 * このソースは実際にサーバーで動いている認証ロジックと同じメモリレイアウト・
 * 同じ判定を再現している。ビルドして手元で試してもよいし、読んで脆弱性を
 * 見抜いて /api/problem5 に直接ペイロードを送ってもよい。
 *
 *   $ gcc -fno-stack-protector -o auth auth.c   # ローカル検証する場合
 *
 * 送信インタフェース（サーバー側 = /api/problem5）:
 *   POST { "payload_hex": "<16進のバイト列>" }
 *   POST { "payload_b64": "<base64のバイト列>" }
 */
#include <stdio.h>
#include <string.h>

/*
 * name[16] のすぐ後ろに is_admin(4byte) が並ぶ。
 * この隣接関係が鍵。
 */
struct session {
    char          name[16];   /* offset  0 .. 15 */
    unsigned int  is_admin;   /* offset 16 .. 19 (little-endian), 初期値 0 */
};

int authenticate(const unsigned char *input, int input_len)
{
    struct session sess;
    sess.is_admin = 0;                 /* 既定では管理者ではない */
    memset(sess.name, 0, sizeof(sess.name));

    /*
     * ↓↓↓ 脆弱性の本体 ↓↓↓
     * 入力長を name[16] と照合せずにコピーしている。
     * 16 バイトを超える入力は隣の is_admin を上書きする（out-of-bounds write / CWE-787）。
     * 本来なら strncpy(sess.name, input, sizeof(sess.name)) 等で切り詰めるべき。
     */
    memcpy(sess.name, input, input_len);   /* NO bounds check! */

    /* 正しいパスワードを知らなくても、is_admin さえ立てば通過できてしまう */
    if (sess.is_admin == 0xDEADBEEF) {
        return 1;   /* 認証成功 → フラグ開示 */
    }
    return 0;
}

int main(void)
{
    unsigned char buf[256];
    int n = read(0, buf, sizeof(buf));     /* 標準入力から生バイトを受け取る想定 */
    if (authenticate(buf, n)) {
        printf("access granted. here is your flag: flag{...}\n");
    } else {
        printf("access denied.\n");
    }
    return 0;
}
