#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/stat.h> #include <pwd.h> #ifdef SHADOW_PASSWD #include <shadow.h> #endif #ifndef APOPPASSWD #define APOPPASSWD "/usr/local/bin/apoppasswd" #endif #ifndef APOPFILEBASE #define APOPFILEBASE ".apop" #endif #ifndef XADDR_DELIM #define XADDR_DELIM ('-') #endif char *myname; int ishexa(int c) { strchr("0123456789ABCDFabcdef", c) ? 1 : 0; } put_form(email, pass, new, new2, suffix, hidden, auth, force) char *email, *pass, *new, *new2, *suffix; int hidden, auth, force; /* auth = 0: old password 1: base addresse's mail password 2: unix password */ { char *authtype[] = {"old", "base", "unix"}; char *var[] = {"email", "pass", "new", "new2", "auth", ""}; char *val[] = {email, pass, new, new2, authtype[auth]}; char *prm[] = {"", /* "ユーザ名", */ auth ? ((auth==1) ? "基本メイルアドレス用パスワード<br>Password for Basic Mail address" : "UNIXログインパスワード<br>UNIX login Password") : "古いメイルパスワード<br>Old Mail Password", "新しいメイルパスワード<br>New Mail Password", "新パスワードをもう一回(確認)<br>New Mail Password Again", ""}; int h=0, i; printf("<form method=POST action\"./%s\">\n", myname); printf(" <table border=1>\n"); for (i=0; var[i][0]; i++) { h = hidden || strstr("email,suffix,auth", var[i]); if (prm[i][0]) { printf("<tr><td>%s</td><td>", prm[i]); } else { } printf("<input name=%s %svalue=\"%s\" length=40 maxlength=40>\n", var[i], h ? "type=hidden " : (strstr(prm[i], "パスワード") ? "type=password " : "<br>"), val[i]); if (!strcmp(var[i], "suffix")) { /* ここでは suffix を入れさせない方がいいかも */ /* 表向きのメイルアドレスを表示しておく */ printf("%s", email); /* if (suffix[0]) { printf("-%s", suffix); } */ if (auth) printf("<br>(新規作成:New Account)"); } if (prm[i][0]) printf("</td></tr>"); printf("\n"); } printf("</table>\n"); if (force) printf("<input name=force type=hidden value=ON>\n"); if (auth) { char *a[] = {"basic", "unix"}; printf("<input type=hidden name=auth value=\"%s\">\n", a[auth-1]); } printf("<input name=OK value=OK type=submit>\n"); printf("<input name=RESET value=RESET type=reset>\n"); printf("</form>\n"); fflush(stdout); } char *decode(char *code) { int l=1+strlen(code); int i, c, d; char *ret = (char*)malloc(l*sizeof(char)); char *p = code; memset(ret, 0, l); for (i=0; i<strlen(code); i++) { if (code[i] == '+') code[i] = ' '; } while (code[0] && (p=strchr(code, '%')) && ishexa(*(p+1)) && ishexa(*(p+2))) { *(p++) = '\0'; strncat(ret, code, l); c = (islower(*p) ? toupper(*p) : *p) - '0'; p++; d = (islower(*p) ? toupper(*p) : *p) - '0'; if (c > 9) c -= ('A'-'9'-1); if (d > 9) d -= ('A'-'9'-1); ret[strlen(ret)] = c*16+d; code = p+1; } if (code[0]) strncat(ret, code, l); return ret; } #define BSIZE 8192 char **decode_post() { char *buf = (char*)malloc(BSIZE*sizeof(char)); char **post, *p = buf; int n=0, i; post = (char**)calloc(1, sizeof(char*)); *buf = '\0'; fgets(buf, BSIZE, stdin); if (strchr("\n\r", buf[strlen(buf)-1])) /* chop */ buf[strlen(buf)-1] = '\0'; while (buf[0] && NULL != (p=strchr(buf, '&'))) { *p = '\0'; post[n] = (char*)malloc((p-buf+1)*sizeof(char)); strcpy(post[n], buf); n++; post = (char**)realloc(post, (1+n)*sizeof(char*)); buf = 1+p; } if (buf[0]) post[n++] = buf; /* decode URL encoded */ for (i=0; i < n; i++) { char *p; p=post[i]; post[i] = decode(p); } post[i] = ""; /* terminator */ return post; } void footer() { puts("</body>\n</html>"); fflush(stdout); } void fail() { printf("パスワード更新に失敗しました<br>\n"); printf("<a href=\"./\">やり直し</a><br>\n"); footer(); exit(1); } void success(char *email) { printf("<hr>メイルアカウント %s 用のパスワード更新は完了しました。<br>\n", email); footer(); exit(0); } int apopfile_existp(char *home, char *suffix, uid_t uid) { struct stat st; int s; int len = strlen(home) + 1 + strlen(APOPFILEBASE) + strlen(suffix) + 3; char *apopfile = (char*)malloc(len); if (suffix[0]) { snprintf(apopfile, len, "%s/%s%c%s%c", home, APOPFILEBASE, XADDR_DELIM, suffix, 0); } else { snprintf(apopfile, len, "%s/%s%c", home, APOPFILEBASE, 0); } seteuid(uid); s = stat(apopfile, &st); seteuid(0); memset(apopfile, '\0', strlen(apopfile)); free(apopfile); return !s; } #ifndef QMAILCONTROL # define QMAILCONTROL "/var/qmail/control" #endif #ifndef MAILTMPLEN # define MAILTMPLEN 1024 #endif /* Convert virtual domain user */ char* conv_virtualdomain(char *account) { char *dom = strchr(account, '@'), *p; char vd[MAILTMPLEN+1], rewrite[MAILTMPLEN+1], previous[MAILTMPLEN+1]; FILE *vdfd; int match=0; char buf[MAILTMPLEN+1], *s; snprintf(vd, MAILTMPLEN, "%s/%s", QMAILCONTROL, "virtualdomains"); if (NULL == dom) return account; dom++; /* set position of domain part beginning */ if (dom && NULL != (vdfd = fopen (vd, "r"))) { int l = strlen(dom); int L = strlen(account); while ((s=fgets(buf, MAILTMPLEN, vdfd))) { if (p=strchr(s, '#')) *p = '\0'; /* zap comments */ if (!strchr(buf, ':')) continue; while (s && (strrchr(s, '\n') || strrchr(s, '\r') || strrchr(s, ' '))) s[strlen(s)-1] = '\0'; if (!strncmp(account, s, L) && s[L] == ':' && s[L+1]) { /* user matches */ match = 3; snprintf(rewrite, MAILTMPLEN, "%s-%s", s+L+1, account); break; } if (!strncmp(dom, s, l) && s[l] == ':' && s[l+1]) { /* domain matches */ match = 2; snprintf(rewrite, MAILTMPLEN, "%s%c%s", s+l+1, XADDR_DELIM, account); continue; } if (match < 2 && s[0] == '.') { /* if domain described in wildcard */ if (p=strchr(s, ':')) { *p = '\0'; if (!strcmp(dom+(strlen(dom)-strlen(s)), s)) { if (match == 0 || strlen(previous) < strlen(s)) { match = 1; strncpy(previous, s, MAILTMPLEN); snprintf(rewrite, MAILTMPLEN, "%s%c%s", p+1, XADDR_DELIM, account); } } } } } fclose(vdfd); if (match) { p = strchr(rewrite, '@'); /* fprintf(stderr, "m=%d, rwr=[%s]\n", match, rewrite); */ if (p) { *p = '\0'; } /* fprintf(stderr, "rwr=[%s]\n", rewrite); */ s = malloc(strlen(rewrite)+1); strncpy(s, rewrite, strlen(rewrite)+1); memset(vd, 0, sizeof(vd)); memset(rewrite, 0, sizeof(rewrite)); memset(previous, 0, sizeof(previous)); return s; } } /* Then, compare with locals */ snprintf(vd, MAILTMPLEN, "%s/%s", QMAILCONTROL, "locals"); if (NULL != (vdfd=fopen(vd, "r"))) { while (s=fgets(buf, MAILTMPLEN, vdfd)) { if (p=strchr(s, '#')) *p = '\0'; /* zap after comment mark # */ while (*s && (strrchr(s, '\r')||strrchr(s, '\n') ||strrchr(s, ' ')||strrchr(s, '\t'))) { *(s+strlen(s)-1) = '\0'; } while (*s && (*s == '\t' || *s == ' ')) s++; if (!strncmp(s, dom, strlen(s))) { /* matches with local domain */ int len = dom-account-1; p = (char*)malloc(len+1); memset(p, '\0', len+1); strncpy(p, account, len); return p; } } } return NULL; /* invalid domain */ /* return account; return itself */ } void apopcall(char **args) { int i=0, sc=0; pid_t pid; char *email="", *suffix="", *pass="", *new="", *new2 = "", *home=""; char buf[BUFSIZ], auth, *user; FILE *child, *result; while (args[i][0]) { /* printf("[%s]<br>\n", args[i]); */ if (!strncmp("email=", args[i], 6)) { email = args[i]+6; } else if (!strncmp("suffix=", args[i], 7)) { suffix = args[i]+7; } else if (!strncmp("pass=", args[i], 5)) { pass = args[i]+5; } else if (!strncmp("new=", args[i], 4)) { new = args[i]+4; } else if (!strncmp("new2=", args[i], 5)) { new2 = args[i]+5; } else if (!strncmp("auth=", args[i], 5)) { /* "this" or "base" or "unix" */ auth = args[i][5]; } i++; } /* Make a backup of original e-mail address */ /* user = (char*)malloc(1+strlen(email)); strcpy(user, email); */ user = conv_virtualdomain(email); if (NULL == user) { printf("そのようなドメインは無効です(%s)<br>\n", strchr(email, '@')); printf("入力したメイルアドレスを確認してやり直してください.<br>\n"); fail(); } if (strchr(user, XADDR_DELIM)) { char *p = malloc(1+strlen(user)); char *q = NULL; struct passwd *pwd; /* printf("user=[%s]<br>\n", user); */ memset(p, '\0', 1+strlen(user)); strcpy(p, user); while (!(pwd=getpwnam(p)) && (q=strrchr(p, XADDR_DELIM))) { fflush(stdout); *q = '\0'; } if (pwd && q) { q = user+(q-p)+1; user=p; suffix=q; } } if (user[0] && new[0] && new2[0]) { int tochild[2], toparent[2]; pid_t pid; int argc=0; char **argv; struct passwd *pswd; char *pstr; if (!(pswd=getpwnam(user))) { printf("Unkown user %s.\n", user); fflush(stdout); fail(); } pstr = pswd->pw_passwd; #ifdef SHADOW_PASSWD { struct spwd *ss = getspnam(user); pstr = (char*)ss->sp_pwdp; } #endif home=pswd->pw_dir; argv = (char**)calloc(4, sizeof(char*)); argv[argc++] = "apoppasswd"; argv[argc++] = "-s"; argv[argc++] = "-c"; /* if old password does not exist, then check UNIX password */ #if 0 if (apopfile_existp(home, suffix, pswd->pw_uid)) { /* no apop-ext exists */ /* そのまま */ } else if (apopfile_existp(home, "", pswd->pw_uid)) {/* check base mail password */ argv = (char**)realloc(argv, (argc+2)*sizeof(char*)); argv[argc++] = "-b"; } #endif switch (auth) { case 'b': case 'B': if (apopfile_existp(home, "", pswd->pw_uid)) { argv = (char**)realloc(argv, (argc+2)*sizeof(char*)); argv[argc++] = "-b"; } else { printf("基本アドレスのパスワードファイルがありません<br>\n"); fail(); } break; case 'u': case 'U': if (strcmp(pstr, (char*)crypt(pass, pstr))) { printf("UNIX Password not correct.<br>\n"); /* printf("[%s]vs.[%s]<br>\n", pswd->pw_passwd, crypt(pass, pswd->pw_passwd)); */ printf("UNIXパスワードと一致しません.<br>\n"); fflush(stdout); fail(); } } if (strlen(new) < 8 || strlen(new2) < 8) { printf("New mail password must be more than 7 characters.<br>\n"); printf("メイルパスワードは8文字以上にしてください。<br>\n"); fflush(stdout); fail(); } if (suffix[0]) { argv = (char**)realloc(argv, (argc+3)*sizeof(char*)); argv[argc++] = "-e"; argv[argc++] = suffix; } argv[argc++] = NULL; if (setgid(pswd->pw_gid) || 0 != setuid(pswd->pw_uid)) { printf("Cannot switch to %s\n", user); printf("uid=%d, gid=%d<br>\n", pswd->pw_gid, pswd->pw_uid); printf("メイルパスワード変更サーバの設定不良の可能性があるので<br>\n"); printf("お手数ですがこの画面のコピーを添えてシステム管理者"); printf("まで御連絡下さい。<br>\n"); fflush(stdout); fail(); } /* OK, start apopasswd */ if (pipe(tochild)+pipe(toparent)) { printf("Cannot create pipe\n"); fail(); } if ((pid=fork()) > 0) { FILE *child = fdopen(tochild[1], "w"); close(tochild[0]); close(toparent[1]); fprintf(child, "PASS %s\nNEW %s\nNEW2 %s\n", pass, new, new2); fflush(child); fclose(child); } else if (pid == -1) { printf("Cannot fork\n"); fail(); } else { char *pe = malloc(6+strlen(pswd->pw_dir)); close(tochild[1]); close(toparent[0]); dup2(tochild[0], 0); dup2(toparent[1], 1); /* setuid section */ strcpy(pe, "HOME="); strcat(pe, pswd->pw_dir); if (putenv(pe)) { puts("ga-n! arichan gakkari<br>"); } execv(APOPPASSWD, argv); /* setuid section ends */ fprintf(stderr, "Cannot exec %s\n", APOPPASSWD); fail(); } result = fdopen(toparent[0], "r"); while (fgets(buf, BUFSIZ, result)) { printf("%s<br>", buf); fflush(stdout); if (strstr(buf, "Success!")) { printf("<br>Mail Password changed successfully!<br>\n"); sc++; break; } else if (strstr(buf, "mismatch")) { printf("二個入れた新パスワードが一致しません.<br>\n"); break; } else if (strstr(buf, "Illegal")) { printf("照合用パスワードが違います.<br>--\n"); break; } else if (strstr(buf, "does not exist")) { /* try_overwrite(user, pass, new, new2, suffix); */ if (suffix[0]) { printf("%s-%s", user, suffix); } else { printf("%s", user); } /* ここは来ないことになった(のはず) */ printf("というメイルアカウントは未作成です<br>\n"); printf("新規に作る場合はOKボタンをクリック\n"); put_form(email, pass, new, new2, suffix, 1, 0, 1); fflush(stdout); } } fclose(result); while (wait(0) != pid) {sleep(1);fputc('.', stderr);} if (sc) success(email); else fail(); } else if (user[0]) { struct passwd *pw = getpwnam(user); int auth=0; if (!pw) { printf("そのようなユーザはいません %s<br>\n", user); fail(); } home=pw->pw_dir; printf("%s というメイルアドレスの<br>\n", email); printf("メイル専用パスワードを変更します.<br>\n"); printf("メイルパスワードとUNIXパスワードの違いに気をつけてください.<br>\n"); printf("新パスワードは8文字以上にしてください.<br>\n"); printf("New password must be more than or equal to 8 characters.<br>\n"); if (apopfile_existp(home, suffix, pw->pw_uid)) { auth = 0; /* this password file */ printf("「古いメイルパスワード」には、現在<br>\n"); printf("<tt>%s</tt><br>\n", email); printf("を読むために指定しているパスワードを入力します。"); } else if (apopfile_existp(home, "", pw->pw_uid)) { auth = 1; /* basic mail address password */ printf("今回は本人認証として基本メイルアドレスのパスワードを"); printf("入力しますが、新しくパスワードを設定するのは<br>\n"); printf("<tt>%s</tt><br>\n", email); printf("用のパスワードです。基本メイルアドレスのパスワードは"); printf("変わりませんので注意してください。"); } else { auth = 2; /* UNIX login */ } put_form(email, "", "", "", suffix, 0, auth, 0); footer(); exit(0); } printf("user=[%s]\n", user); } int main(int argc, char* argv[]) { char *method = getenv("REQUEST_METHOD"); char **args; myname = argv[0]; if (method && strcmp(method, "POST") != 0) { printf("This program should be used in method:POST.\n"); fail(); } printf("Content-type: text/html; charset=EUC-JP\n\n"); printf("<html>\n<head><title>Change Password</title></head>\n"); printf("<body style=\"background: #f0ffff;\">\n"); if (getenv("SSL_CIPHER") && getenv("SSL_PROTOCOL")) { args = decode_post(); apopcall(args); } else { printf("This program can be used only via SSL connection.<br>\n"); printf("このユーティリティはSSL接続時のみ有効です.<br>\n"); } }