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");
+    }
+}

yatex.org