/* * password changer version 1.0 written by fuku@rouge.gr.jp /etc/passwd の shell に関係なく /usr/bin/passwd を起動するプログラム。 xinetd (あるいは inetd) から起動します。 コンパイル方法: $ gcc -lutil -lcrypt -o chpassd chpassd.c $ chmod 755 chpassd $ su # chown root:root chpassd # cp -p chpassd /usr/local/sbin/. 設定方法: # vi /etc/services chpassd 33/tcp # vi /etc/xinetd.d/chpassd service chpassd { flags = REUSE socket_type = stream wait = no user = root server = /usr/local/sbin/chpassd disable = no } # service xinetd restart # vi /etc/hosts.allow chpassd: LOCAL * */ #include #include #include #include #include #include #include typedef enum { FAIL = 0, SUCCESS = 1 } auth_result ; typedef enum { OFF = 0, ON = 1 } off_on ; // /etc/shadow による認証 // root しか見られないファイルなので、 // 一般ユーザで実行すると必ず FAIL になります。 auth_result auth_shadow (const char* user, const char* pass) { struct spwd* spwd ; auth_result result ; result = SUCCESS ; //setspent() ; spwd = getspnam(user) ; if (spwd == NULL) { //fprintf(stderr, "spwd: %s\n", spwd) ; result = FAIL ; } else if (strcmp((char* )crypt(pass, spwd->sp_pwdp), spwd->sp_pwdp)) result = FAIL ; endspent() ; return result ; } // 文字列を読み込む // '\r' で終了です。(続く '\n' が邪魔なので読み飛ばします) size_t read_string(int fd, void* buf, size_t count, off_on sw) { unsigned char c ; size_t len ; len = 0 ; while (len < count) { read(fd, &c, 1) ; if (c == '\r') { read(fd, &c, 1) ; // 続く '\n' を読み飛ばす break ; } else { ((unsigned char* )buf)[len++] = c ; if (sw == ON) write(STDOUT_FILENO, &c, 1) ; // エコーバック } } return len ; } int main () { int from_child ; unsigned char dmy[3] ; fd_set check_fd ; char buf_user[1024] ; char buf_pass[1024] ; char* p ; struct passwd* pwd ; // TELNET クライアントに条件を渡す send(STDOUT_FILENO, "\xff\xfb" "\x01", 3, 0); // ECHOの有効を知らせる recv(STDIN_FILENO, dmy, 3, 0); // FF FD 01 が返ります send(STDOUT_FILENO, "\xff\xfb" "\x03", 3, 0); // SGAの有効を知らせる recv(STDIN_FILENO, dmy, 3, 0); // FF FD 03 が返ります #if 1 #define SIGNATURE "** password change service version 1.0 written by fuku@rouge.gr.jp **\r\n" write(STDOUT_FILENO, SIGNATURE, strlen(SIGNATURE)) ; #endif // ユーザ名を聞く #define LOGIN_QUEST "login: " write(STDOUT_FILENO, LOGIN_QUEST, strlen(LOGIN_QUEST)) ; bzero(buf_user, sizeof buf_user) ; read_string(STDIN_FILENO, buf_user, (sizeof buf_user) -1, ON) ; write(STDOUT_FILENO, "\r\n", 2) ; // パスワードを聞く(今のやつ) #define PASS_QUEST "password: " write(STDOUT_FILENO, PASS_QUEST, strlen(PASS_QUEST)) ; bzero(buf_pass, sizeof buf_pass) ; read_string(STDIN_FILENO, buf_pass, (sizeof buf_pass) -1, OFF) ; write(STDOUT_FILENO, "\r\n", 2) ; // ユーザを認証する if (auth_shadow(buf_user, buf_pass) == FAIL) { sleep(5) ; return 0 ; } // ユーザの UID, GID を取得する pwd = getpwnam(buf_user) ; if (pwd == NULL) return 0 ; // 実行権限をそのユーザに変更する setgid(pwd->pw_gid) ; setuid(pwd->pw_uid) ; // 擬似端末と子プロセス (passwd コマンド) の作成 if (forkpty(&from_child, NULL, NULL, NULL) == 0) execl("/usr/bin/passwd", "passwd", NULL) ; // 子プロセス while (1) { FD_ZERO(&check_fd) ; FD_SET(from_child, &check_fd) ; FD_SET(STDIN_FILENO, &check_fd) ; if (select(FD_SETSIZE, &check_fd, NULL, NULL, NULL) < 1) break ; if (FD_ISSET(from_child, &check_fd)) { // 子プロセスからの表示をストリーム (TELNET) へ渡す char buf[1024] ; int len ; if ((len = read(from_child, buf, sizeof buf)) == -1) break ; write(STDOUT_FILENO, buf, len) ; } else { // ストリーム (TELNET) からの入力を子プロセスへ渡す char buf[1024] ; int len ; if ((len = read(STDIN_FILENO, buf, sizeof buf)) == -1) break ; write(from_child, buf, len) ; } } return 0 ; }