/********************************************************************* usermod.cc パスワード変更CGIです。 ご自由にお使い下さい。 g++ -Wall usermod.cc -o usermod.cgi -lcrypt のようにしてコンパイルし,suid root して使います。 詳しくは http://www.matsusaka-u.ac.jp/~okumura/cplusplus/passwd.html をご覧ください。 Haruhiko Okumura [2003-04-30] 小山さんのパッチを取り入れさせていただきました。 *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include /* glibc ベースの Linux はこれが必要 */ #include using namespace std; map post; bool suid; void putheader(const char* msg) { cout << "Content-type: text/html\n\n" "\n" "\n" "\n" "" << msg << "\n" "\n" "

" << msg << "

"; } void msgexit(const char* msg) { if (suid) { setuid(getuid()); suid = false; } putheader(msg); cout << "[戻る]\n"; exit(0); } /** ユーザ名 user, パスワード pass を検証する。 正しいユーザであれば gid を返し, そうでなければ負の値を返す。 スーパーユーザでなければならない。 */ int auth_user(const char *user, const char *pass) { struct passwd *pw; /* passwd entry */ struct spwd *spw; /* shadow entry */ char buf[14]; /* encrypted passwd */ int gid; /* gid of the user */ /* look for the user in the password file */ /* need not be a superuser */ if ((pw = getpwnam(user)) == NULL) { /* Not listed in passwd */ /* sleep(3); */ return -1; } /* uid = pw->pw_uid; */ gid = pw->pw_gid; /* must be a superuser */ setspent(); /* begin access to shadow */ /* Look for the user in the shadow password file */ if ((spw = getspnam(user)) == NULL) { /* Not listed in shadow (or you are not a superuser) */ sleep(3); return -2; } strncpy(buf, spw->sp_pwdp, sizeof(buf)); endspent(); /* end access to shadow */ if (strcmp(crypt(pass, buf), buf) != 0) { /* wrong passwd */ sleep(3); return -3; } return gid; } void parse_post() { while (cin) { string s, t; int c; while ((c = cin.get()) != EOF && c != '=') { s += char(c); } if (c == EOF) break; while ((c = cin.get()) != EOF && c != '&') { if (c == '+') { t += ' '; } else if (c == '%') { int d = cin.get(); int e = cin.get(); d = (d >= 'A') ? (d & 0xdf) - 'A' + 10 : d - '0'; e = (e >= 'A') ? (e & 0xdf) - 'A' + 10 : e - '0'; if (d >= 0 && d < 16 && e >= 0 && e < 16) { t += char(16 * d + e); } else { t += char(c); t += char(d); t += char(e); } } else { t += char(c); } } post[s] = t; } } char* salt() { static char s[3]; char a[65] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789./"; unsigned t = time(0); s[0] = a[t % 64]; s[1] = a[(t / 64) % 64]; s[2] = 0; return s; } void checkpass(string s) { if (s.length() < 6 || s.length() > 8) msgexit("パスワードは6〜8文字にしてください"); unsigned lower = 0; unsigned upper = 0; unsigned num = 0; for (string::const_iterator p = s.begin(); p != s.end(); ++p) { if (*p < 0x21 || *p > 0x7e) msgexit("パスワードに不正な文字が含まれています"); if (*p >= 'a' && *p <= 'z') lower++; if (*p >= 'A' && *p <= 'Z') upper++; if (*p >= '0' && *p <= '9') num++; } if (lower == s.length() || upper == s.length() || num == s.length()) msgexit("パスワードが単純すぎます。" "アルファベットと数字等を混ぜてください。"); } int main() { suid = true; // root に setuid されている parse_post(); if (post["user"].length() < 3 || post["user"].length() > 16) msgexit("ユーザ名の長さが変です"); if (post["oldpass"].length() < 3) msgexit("旧パスワードが短すぎます"); if (post["newpass1"] != post["newpass2"]) msgexit("二つの新パスワードが一致しません"); if (post["oldpass"] == post["newpass1"]) msgexit("新旧パスワードが同じです"); checkpass(post["newpass1"]); int gid = auth_user(post["user"].c_str(), post["oldpass"].c_str()); if (gid == -1) msgexit("ユーザ名が登録されていません"); if (gid == -2) msgexit("システムエラー(シャドウファイルが読めません)"); if (gid == -3) msgexit("パスワードが違います"); if (gid < 10) msgexit("あなたのパスワードの変更は管理人しかできません"); char* pass = crypt(post["newpass1"].c_str(), salt()); /* char buffer[1024]; sprintf(buffer, "/usr/sbin/usermod -p '%s' '%s'", pass, post["user"].c_str()); system(buffer); msgexit("パスワードを変更しました"); */ if (fork() == 0) { execl("/usr/sbin/usermod", "/usr/sbin/usermod", "-p", pass, post["user"].c_str(), NULL); exit(127); } int status; wait(&status); if (status == 0) msgexit("パスワードを変更しました"); else msgexit("パスワード変更に失敗しました"); }