/********************************************************************* forward.cc メール回送設定CGIです。 ご自由にお使い下さい。 g++ -Wall forward.cc -o forward.cgi -lcrypt のようにしてコンパイルし,suid root して使います。 詳しくは http://www.matsusaka-u.ac.jp/~okumura/cplusplus/forward.html をご覧ください。 Haruhiko Okumura *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include /* glibc ベースの Linux はこれが必要 */ using namespace std; map post; bool suid; int uid; int gid; char* homedir; 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 を検証する。 スーパーユーザでなければならない。 */ void auth_user(const char *user, const char *pass) { struct passwd *pw; /* passwd entry */ struct spwd *spw; /* shadow entry */ char buf[14]; /* encrypted passwd */ /* look for the user in the password file */ /* need not be a superuser */ if ((pw = getpwnam(user)) == NULL) { /* Not listed in passwd */ /* sleep(3); */ msgexit("ユーザ名が登録されていません"); } uid = pw->pw_uid; gid = pw->pw_gid; homedir = pw->pw_dir; /* 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); msgexit("システムエラー(シャドウファイルが読めません)"); } strncpy(buf, spw->sp_pwdp, sizeof(buf)); endspent(); /* end access to shadow */ if (strcmp(crypt(pass, buf), buf) != 0) { /* wrong passwd */ sleep(3); msgexit("パスワードが違います"); } } 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; } } void checkaddr(string s) { int atcount = 0; for (string::const_iterator p = s.begin(); p != s.end(); ++p) { if (*p == '@') { atcount++; } else if (*p >= 'a' && *p <= 'z') { // good } else if (*p >= 'A' && *p <= 'Z') { // good } else if (*p >= '0' && *p <= '9') { // good } else if (*p == '.' || *p == '_' || *p == '-') { // good } else { msgexit("アドレスに不正な文字が含まれています"); } } if (atcount == 0) msgexit("アドレスに @ がありません"); if (atcount > 1) msgexit("アドレスに @ が複数あります"); } int main() { suid = true; // root に setuid されている parse_post(); if (post["user"].length() < 3 || post["user"].length() > 16) msgexit("ユーザ名の長さが変です"); if (post["pass"].length() < 3) msgexit("パスワードが短すぎます"); if (post["email"].length() != 0) checkaddr(post["email"]); auth_user(post["user"].c_str(), post["pass"].c_str()); string path = string(homedir) + "/.forward"; if (post["email"].length() != 0) { ofstream out(path.c_str()); if (!out) msgexit(".forward が開きません"); if (post["keep"] == string("on")) out << '\\' << post["user"] << ", "; out << post["email"] << '\n'; out.close(); chown(path.c_str(), uid, gid); } else { remove(path.c_str()); } msgexit("変更しました"); }