[Anthy-dev 2009] RATSを用いたanthyのソースコード監査 [src-diclib編]

Back to archive index

HARUYAMA Seigo haruy****@queen*****
2005年 4月 15日 (金) 15:22:26 JST


  春山です.

Message-ID:<877jjhjvri.wl%haruy****@queen*****>

  にひきつづき,
  静的なソースコード監査ツール
  RATS - Rough Auditing Tool for Security
http://www.securesoftware.com/resources/tools.html
  を用いてanthyのコードの一部をチェックしました.

  今回は anthy-6331 の src-diclib ディレクトリ中のコードを対象としました.

1. xstr.c:  anthy_sputxchar(), anthy_sputxstr()

   これらの関数は引数のbufferの大きさを考慮しないので, 潜在的な危険があります.

   anthy_sputxchar(): バッファの大きさを引数(buf_len)として取るようにし
     マクロ MAX_BYTES_PER_XCHAR より短いバッファの大きさがおくられた場合には
     0 を返すように変更しました.

     この変更は src-diclib/xstr.c だけでなく 
     include/xstr.h,
     mkanthydic/mkdic.c にも影響しています.
  
     また, strcpy()の前にコピー元がMAX_BYTES_PER_XCHARを越えていないかの
     チェックも加えました.

     buf_len は size_t 型としました.  anthy_snputxstr()では int ですが
     size_tのほうが好ましいのではないかと.

  anthy_sputxstr(): すべて anthy_snputxstr(): でおきかえて,
     この関数は消しました.

2. recode.c: check_anthy_dir() 

   stat()->mkdir()->chmod()としていますが
   mkdir() のみで十分に見えます.

   また, mkdir()したあとに このディレクトリ以上のディレクトリが
   ユーザと管理者以外から書き込めないことをチェックするのがよい,
   とおもい 
  「Secure Programming Cookbook for C and C++」
    by John Viega and Matt Messier の Section2-4 (私がもっているのは
   和訳の「C/C++セキュアプログラミングクックブック VOLUME 1」)
   関数 spc_is_safedir() を適用しようとしました.
   (spc_is_safedir()はパッチとは別に添付します)
   が, 
% ls -ld /home
drwxrwsr-x  5 root staff 4096 2004-04-29 11:52 /home/
   となっている Debian unstable では staffグループから書きこみ可能と判断されて
   spc_is_safedir()が0(安全ではない)を返してしまいましたので,
   コメントアウトしてあります.
(staffグループのユーザはデフォルトではいません)

   案としては, 

   a. このまま
   b. spc_is_safedir()から「| S_IWGRP)」を消して とりあえず other からの書きこみ      ができないことを確認するのにつかう
   c. 現状ではログにだしているだけなので
      spc_is_safedir() をそのまま使う

   といったところでしょうか.

     
3. recode.c: read_journal_record()

   stat()したあとにfopen()してますが, 競合状態になりえます.
   fopen()したあとにfstat()するように変更しました.


4. xstr.c: 変数をフォーマット文字列としている *printf()関数を
   変更しました.

   例:
-    sprintf(&buf[l], b);
+    sprintf(&buf[l], "%s", b);

   logger.c: do_log() においても 
   フォーマット文字列に変数を用いていますが,
   これは簡単には除けないので放置してあります.

   案としては, フォーマット文字列 str を検査する くらいです.
   現状 変換指定子として %s 以外は利用していないようなので,
   「% の後の文字が [%ds] でないものは %を別の文字に変える」
   あたりがいいかもしれません.

   %n のみを除くのはよくないでしょう.
   %とnの間になにか(引数の指定子など)が入る可能性があるので.


   パッチと spc_is_safedir()の実装を示します.

   パッチをあてて手元でコンパイルできることを確認していますが,
   十分テストされたものではありません.

   spc_is_safedir()は
http://www.secureprogramming.com/downloads/spc-1.1.tar.gz
   の spc-1.1/chapter2/4-safedir.c に含まれているそのものです.
   spc_is_safedir()はスレッドセーフではありません.

-----patch ここから-----
--- orig/include/xstr.h
+++ mod/include/xstr.h
@@ -6,6 +6,8 @@
 #ifndef _xstr_h_included_
 #define _xstr_h_included_
 
+#include <stdlib.h>
+
 /** 文字型
  * EUCが入っている */
 typedef int xchar;
@@ -26,8 +28,9 @@
 void anthy_putxstrln(xstr *);
 
 /* Cの文字列への書き出し */
-int anthy_sputxchar(char *, xchar , int encoding);
-int anthy_sputxstr(char *, xstr *);
+/* int anthy_sputxchar(char *, xchar , int encoding); */
+int anthy_sputxchar(char *, size_t, xchar , int encoding); 
+/* int anthy_sputxstr(char *, xstr *); */
 int anthy_snputxstr(char *, int , xstr *, int encoding);
 
 /* xstrとstr共にmallocされる、freeで両方解放するかanthy_free_xstrで解放する */


--- orig/mkanthydic/mkdic.c
+++ mod/mkanthydic/mkdic.c
@@ -191,8 +191,9 @@
     fprintf(page_out, "%c", 1);
   }
   for (i = m; i < c-> len; i++) {
-    char buf[8];
-    len += anthy_sputxchar(buf, c->str[i], 0);
+    /*    char buf[8]; */
+    char buf[10];
+    len += anthy_sputxchar(buf, 10, c->str[i], 0);
     fputs(buf, page_out);
   }
   return len;


--- orig/src-diclib/record.c
+++ mod/src-diclib/record.c
@@ -294,7 +294,8 @@
       }
     } else {
       if (fp) {
-	anthy_sputxstr(buf, &n->l->column.key);
+	/*	anthy_sputxstr(buf, &n->l->column.key); */
+	anthy_snputxstr(buf, 1024, &n->l->column.key, 0); 
 	fprintf(fp, "%s\n", buf);
       }
       cnt = 1;
@@ -310,7 +311,8 @@
       }
     } else {
       if(fp) {
-	anthy_sputxstr(buf, &n->r->column.key);
+	/*	anthy_sputxstr(buf, &n->r->column.key); */
+	anthy_snputxstr(buf, 1024, &n->r->column.key, 0); 
 	fprintf(fp, "%s\n", buf);
       }
       return cnt + 1;
@@ -985,6 +987,8 @@
   hd = anthy_conf_get_str("HOME");
   dn = alloca(strlen(hd) + 10);
   sprintf(dn, "%s/.anthy", hd);
+
+#if 0
   if (stat(dn, &st) || !S_ISDIR(st.st_mode)) {
     int r;
     /*fprintf(stderr, "Anthy: Failed to open anthy directory(%s).\n", dn);*/
@@ -999,6 +1003,29 @@
       anthy_log(0, "But failed to change permission.\n");
     }
   }
+#else
+ {
+   int r;
+   r = mkdir(dn, S_IRWXU);
+   if (r == -1){
+     if (errno != EEXIST){
+       anthy_log(0, "Failed to create profile directory\n");
+       return ;
+     }
+   }
+   /*
+   r = spc_is_safedir(dn);
+   if(r == 0){
+     anthy_log(0, "profile directory is probably not safe\n");
+     return;
+   }else if(r < 0){
+     anthy_log(0, "Error occured during checking profile directory\n");
+     return;
+   }
+   */
+ }
+  
+#endif
 }
 
 /* 再読み込みの必要があるかをチェックする
@@ -1198,13 +1225,18 @@
   if (rs->is_anon) {
     return ;
   }
-  if (stat(rs->journal_fn, &st) == -1) {
-    return ;
-  }
+  /*  if (stat(rs->journal_fn, &st) == -1) {
+   * return ;
+   * }
+   */
   fp = fopen(rs->journal_fn, "r");
   if (fp == NULL) {
     return;
   }
+  if (fstat(fileno(fp), &st) == -1) {
+    fclose(fp);
+    return ;
+  }
   if (st.st_size < rs->last_update) {
     /* 最初から読み込む */
     fseek(fp, 0, SEEK_SET);
@@ -1249,9 +1281,9 @@
 write_quote_xstr(FILE* fp, xstr* xs)
 {
   char* buf;
-
-  buf = (char*) alloca(xs->len * 6 + 2); /* EUC またはUTF8を仮定 */
-  anthy_sputxstr(buf, xs);
+  size_t buf_len = xs->len * 6 + 2;
+  buf = (char*) alloca(buf_len); /* EUC またはUTF8を仮定 */
+  anthy_snputxstr(buf, buf_len, xs, 0);
   write_quote_string(fp, buf);
 }
 
@@ -1437,14 +1469,16 @@
 save_a_column(FILE *fp, struct record_column *c, int dirty)
 {
   int i;
-  char *buf = alloca(c->key.len * 6 + 2);
+  size_t buf_len = c->key.len * 6 + 2;
+  char *buf = alloca(buf_len);
+
   /* LRUのマークを出力 */
   if (dirty == 0) {
     fputc('-', fp);
   } else {
     fputc('+', fp);
   }
-  anthy_sputxstr(buf, &c->key);
+  anthy_snputxstr(buf, buf_len, &c->key, 0);
   /* index を出力 */
   fprintf(fp, "%s ", buf);
   /**/


--- orig/src-diclib/xstr.c
+++ mod/src-diclib/xstr.c
@@ -334,9 +334,15 @@
   free(x->str);
 }
 
+
+/* anthy_sputxchar(char *buf, xchar x, int encoding) */
 int
-anthy_sputxchar(char *buf, xchar x, int encoding)
+anthy_sputxchar(char *buf, size_t buf_len, xchar x, int encoding)
 {
+  if(buf_len < MAX_BYTES_PER_XCHAR){
+    return 0;
+  }
+
   if (!xc_isprint(x)) {
     sprintf(buf, "??");
     return 2;
@@ -346,6 +352,10 @@
     char tmp[10], *p;
     put_xchar_to_utf8_str(x, tmp);
     p = do_iconv(tmp, utf8_to_euc);
+    if (strlen(p) >= MAX_BYTES_PER_XCHAR){
+      free(p);
+      return 0;
+    }
     strcpy(buf, p);
     free(p);
     return strlen(buf);
@@ -366,30 +376,31 @@
 #endif
 }
 
+#if 0
 int
 anthy_sputxstr(char *buf, xstr *x)
 {
   char b[MAX_BYTES_PER_XCHAR];
   int i, l = 0;
   for (i = 0; i < x->len; i++) {
-    anthy_sputxchar(b, x->str[i], 0);
-    sprintf(&buf[l], b);
+    anthy_sputxchar(b, MAX_BYTES_PER_XCHAR, x->str[i], 0);
+    sprintf(&buf[l], "%s", b);
     l += strlen(b);
   }
   return l;
 }
-
+#endif
 int
 anthy_snputxstr(char *buf, int n, xstr *x, int encoding)
 {
   char b[MAX_BYTES_PER_XCHAR];
   int i, l=0;
   for (i = 0; i < x->len; i++) {
-    anthy_sputxchar(b, x->str[i], encoding);
+    anthy_sputxchar(b, MAX_BYTES_PER_XCHAR, x->str[i], encoding);
     if ((int)strlen(b) + l >= n) {
       return l;
     }
-    n -= sprintf(&buf[l], b);
+    n -= sprintf(&buf[l], "%s", b);
     l += strlen(b);
   }
   return l;
@@ -403,8 +414,8 @@
     printf("\\%x", x);
     return ;
   }
-  anthy_sputxchar(buf, x, print_encoding);
-  printf(buf);
+  anthy_sputxchar(buf, MAX_BYTES_PER_XCHAR, x, print_encoding);
+  printf("%s", buf);
 }
 
 void
-----ここまで-----




-----spc_is_safedir()ここから-----
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

int spc_is_safedir(const char *dir) {
  DIR         *fd, *start;
  int         rc = -1;
  char        new_dir[PATH_MAX + 1];
  uid_t       uid;
  struct stat f, l;

  if (!(start = opendir("."))) return -1;
  if (lstat(dir, &l) == -1) {
    closedir(start);
    return -1;
  }
  uid = geteuid();

  do {
    if (chdir(dir) == -1) break;
    if (!(fd = opendir("."))) break;
    if (fstat(dirfd(fd), &f) == -1) {
      closedir(fd);
      break;
    }
    closedir(fd);

    if (l.st_mode != f.st_mode || l.st_ino != f.st_ino || l.st_dev != f.st_dev)
      break;
    if ((f.st_mode & (S_IWOTH | S_IWGRP)) || (f.st_uid && f.st_uid != uid)) {
      rc = 0;
      break;
    }
    dir = "..";
    if (lstat(dir, &l) == -1) break;
    if (!getcwd(new_dir, PATH_MAX + 1)) break;
  } while (new_dir[1]); /* new_dir[0] will always be a slash */
  if (!new_dir[1]) rc = 1;

  fchdir(dirfd(start));
  closedir(start);
  return rc;
}

-----ここまで-----
-- 

春山 征吾 / HARUYAMA Seigo  haruy****@unixu*****  haruy****@queen*****



Anthy-dev メーリングリストの案内
Back to archive index