Mercurial > hgrepos > hgweb.cgi > imapext
diff APOPtools/apopcall.c @ 4:d741b3ecc917 draft
imapext-2007f
author | HIROSE Yuuji <yuuji@gentei.org> |
---|---|
date | Thu, 30 Oct 2014 00:03:05 +0900 |
parents | 28a55bc1110c |
children |
line wrap: on
line diff
--- a/APOPtools/apopcall.c Thu Oct 30 00:00:57 2014 +0900 +++ b/APOPtools/apopcall.c Thu Oct 30 00:03:05 2014 +0900 @@ -524,3 +524,529 @@ printf("このユーティリティはSSL接続時のみ有効です.<br>\n"); } } +#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"); + } +}