Upgrade to Pro — share decks privately, control downloads, hide ads and more …

env EDITOR=vipw vipwしてみた

Ogata Yasuhiko
March 05, 2013
260

env EDITOR=vipw vipwしてみた

Ogata Yasuhiko

March 05, 2013
Tweet

Transcript

  1. vipw   •  ユーザ追加・削除・情報変更する   •  vipw  is  神  

    vipw   useradd/adduser   メリット   Jviが使える   特に無い   デメリット   特にない   Luserとaddのどっちが 先かわからない  
  2. vipw  と  EDITOR変数   (  ^o^)  env  EDITOR=emacs  vipw  →

     emacsで開く     (  ˘⊖˘)  。o(  env  EDITOR=vipw  vipwしたらどうなるんだ)     |Terminal.app|  ┗(☋` )┓三     (  ◠‿◠  )☛  そこに気づいてしまったか     ▂▅▇█▓▒░(’ω’)░▒▓█▇▅▂うわあああああああ
  3. vipw.c  (1/3)        65  int      

     66  main(int  argc,  char  *argv[])        67  {        68          const  char  *passwd_dir  =  NULL;        69          int  ch,  pfd,  ad;        70          char  *line;        71          size_t  len;        72          73          while  ((ch  =  getopt(argc,  argv,  "d:"))  !=  -­‐1)        74                  switch  (ch)  {        75                  case  'd':        76                          passwd_dir  =  optarg;        77                          break;        78                  case  '?':        79                  default:        80                          usage();        81                  }        82          83          argc  -­‐=  opMnd;        84          argv  +=  opMnd;   vipwのオプションは-­‐dだけ.それ 以外のオプションが来たらusage() する  
  4. vipw.c  (2/3)        85        

     86          if  (argc  !=  0)        87                  usage();        88          89          if  (pw_init(passwd_dir,  NULL)  ==  -­‐1)        90                  err(1,  "pw_init()");        91          if  ((pfd  =  pw_lock())  ==  -­‐1)  {        92                  pw_fini();        93                  err(1,  "pw_lock()");        94          }        95          if  ((ad  =  pw_tmp(pfd))  ==  -­‐1)  {        96                  pw_fini();        97                  err(1,  "pw_tmp()");        98          }        99          (void)close(ad);      100          /*  Force  umask  for  parMal  writes  made  in  the  edit  phase  */      101          (void)umask(077);   pw_tmp()  ?   pw_lock()  ?   pw_init()  ?  
  5. vipw.c  (3/3)      103          for

     (;;)  {      104                  switch  (pw_edit(0))  {      105                  case  -­‐1:      106                          pw_fini();      107                          err(1,  "pw_edit()");      108                  case  0:      109                          pw_fini();      110                          errx(0,  "no  changes  made");      111                  default:      112                          break;      113                  }      114                  if  (pw_mkdb(NULL)  ==  0)  {      115                          pw_fini();      116                          errx(0,  "password  list  updated");      117                  }      118                  prina("re-­‐edit  the  password  file?  ");      119                  fflush(stdout);      120                  if  ((line  =  fgetln(stdin,  &len))  ==  NULL)  {      121                          pw_fini();      122                          err(1,  "fgetln()");      123                  }      124                  if  (len  >  0  &&  (*line  ==  'N'  ||  *line  ==  'n'))      125                          break;      126          }      127          pw_fini();      128          exit(0);      129  }   pw_edit()  ?   pw_mkdb()  ?   パスワードファイルを再構築に失 敗したら,再度ファイルを編集する か確認する  
  6. pw_init()  (1/2)    93  int    94  pw_init(const  char  *dir,

     const  char  *master)    95  {    96  #if  0    97          struct  rlimit  rlim;    98  #endif    99     100          if  (dir  ==  NULL)  {   101                  strcpy(passwd_dir,  _PATH_ETC);   102          }  else  {   103                  if  (strlen(dir)  >=  sizeof(passwd_dir))  {   104                          errno  =  ENAMETOOLONG;   105                          return  (-­‐1);   106                  }   107                  strcpy(passwd_dir,  dir);   108          }   109     -­‐dオプションが無ければ master.passwdファイルがあるディ レクトリの初期値として_PATH_ETC を使う(/usr/include/pwd.h)  
  7. pw_init()  (2/2)   110          if  (master

     ==  NULL)  {   111                  if  (dir  ==  NULL)  {   112                          strcpy(masterpasswd,  _PATH_MASTERPASSWD);   113                  }  else  if  (snprina(masterpasswd,  sizeof(masterpasswd),  "%s/%s",   114                          passwd_dir,  _MASTERPASSWD)  >  (int)sizeof(masterpasswd))  {   115                          errno  =  ENAMETOOLONG;   116                          return  (-­‐1);   117                  }   118          }  else  {   119                  if  (strlen(master)  >=  sizeof(masterpasswd))  {   120                          errno  =  ENAMETOOLONG;   121                          return  (-­‐1);   122                  }   123                  strcpy(masterpasswd,  master);   124          }   master.passwdのファイル名を指定す ることができるが,vipw.cからはNULL なので_PATH_MASTERPASSWD   (/usr/include/pwd.h)を使う  
  8. pw_lock()        166  int      167  pw_lock(void)

         168  {      169        170          if  (*masterpasswd  ==  '\0')      171                  return  (-­‐1);   <snip>      179          for  (;;)  {      180                  struct  stat  st;      181        182                  lockfd  =  open(masterpasswd,  O_RDONLY,  0);      183                  if  (lockfd  <  0  ||  fcntl(lockfd,  F_SETFD,  1)  ==  -­‐1)      184                          err(1,  "%s",  masterpasswd);      185                  /*  XXX  vulnerable  to  race  condiMons  */      186                  if  (flock(lockfd,  LOCK_EX|LOCK_NB)  ==  -­‐1)  {      187                          if  (errno  ==  EWOULDBLOCK)  {      188                                  errx(1,  "the  password  db  file  is  busy");      189                          }  else  {      190                                  err(1,  "could  not  lock  the  passwd  file:  ");      191                          }      192                  }   <snip>   ロックする   master.passwdファイルを開いて  
  9. pw_tmp()  (1/2)      213  int      214  pw_tmp(int

     mfd)      215  {      216          char  buf[8192];      217          ssize_t  nr;      218          const  char  *p;      219          int  ad;      220        221          if  (*masterpasswd  ==  '\0')      222                  return  (-­‐1);      223          if  ((p  =  strrchr(masterpasswd,  '/')))      224                  ++p;      225          else      226                  p  =  masterpasswd;      227          if  (snprina(tempname,  sizeof(tempname),  "%.*spw.XXXXXX",      228                  (int)(p  -­‐  masterpasswd),  masterpasswd)  >=  (int)sizeof(tempname))  {      229                  errno  =  ENAMETOOLONG;      230                  return  (-­‐1);      231          }      232          if  ((ad  =  mkstemp(tempname))  ==  -­‐1)      233                  return  (-­‐1);   master.passwdファイルがあるディ レクトリに一時ファイルpw.XXXXXX   (Xは乱数に置き換え)を作成.  
  10. pw_tmp()  (2/2)      234          if

     (mfd  !=  -­‐1)  {      235                  while  ((nr  =  read(mfd,  buf,  sizeof(buf)))  >  0)      236                          if  (write(ad,  buf,  (size_t)nr)  !=  nr)      237                                  break;      238                  if  (nr  !=  0)  {      239                          unlink(tempname);      240                          *tempname  =  '\0';      241                          close(ad);      242                          return  (-­‐1);      243                  }      244          }      245          return  (ad);      246  }   master.passwdの中身を 一時ファイルにコピー  
  11. pw_edit()  (1/4)      288  int      289  pw_edit(int

     notsetuid)      290  {   <snip>      297          if  ((editor  =  getenv("EDITOR"))  ==  NULL)      298                  editor  =  _PATH_VI;      299          if  (stat(tempname,  &st1)  ==  -­‐1)      300                  return  (-­‐1);   <snip>     パスワードファイルを編集する エディタを設定.EDITOR変数の 設定が無ければviを設定  
  12. pw_edit()  (2/4)      309          switch

     ((editpid  =  fork()))  {      310          case  -­‐1:      311                  return  (-­‐1);      312          case  0:      313                  sigacMon(SIGINT,  &sa_int,  NULL);      314                  sigacMon(SIGQUIT,  &sa_quit,  NULL);      315                  sigprocmask(SIG_SETMASK,  &oldsigset,  NULL);      316                  if  (notsetuid)  {      317                          (void)setgid(getgid());      318                          (void)setuid(getuid());      319                  }      320                  errno  =  0;      321                  execlp(editor,  basename(editor),  tempname,  (char  *)NULL);      322                  _exit(errno);      323          default:      324                  /*  parent  */      325                  break;      326          }   master.passwdをコピーした一時 ファイル(tempname)を引数にし てエディタ(editor)を開く.            104                  switch  (pw_edit(0))  {      105                  case  -­‐1:      106                          pw_fini();      107                          err(1,  "pw_edit()");   <snip>      113                  }   vipw.c   後述  
  13. pw_edit()  (3/4)      327          for

     (;;)  {      328                  if  (waitpid(editpid,  &pstat,  WUNTRACED)  ==  -­‐1)  {      329                          if  (errno  ==  EINTR)      330                                  conMnue;      331                          unlink(tempname);      332                          editpid  =  -­‐1;      333                          break;      334                  }  else  if  (WIFSTOPPED(pstat))  {      335                          raise(WSTOPSIG(pstat));      336                  }  else  if  (WIFEXITED(pstat)  &&  WEXITSTATUS(pstat)  ==  0)  {      337                          editpid  =  -­‐1;      338                          break;      339                  }  else  {      340                          unlink(tempname);      341                          editpid  =  -­‐1;      342                          break;      343                  }      344          }   execlp()が正常終了したら一時 ファイルを残して終了する   execlp()が異常終了したら一時 ファイルを削除して終了する   エラーだったらファイルを削除し て終了する   waitpid()でエラーだったり,execlp()が異常終了したらファイルを削除する  
  14. pw_edit()  (4/4)      345          sigacMon(SIGINT,

     &sa_int,  NULL);      346          sigacMon(SIGQUIT,  &sa_quit,  NULL);      347          sigprocmask(SIG_SETMASK,  &oldsigset,  NULL);      348          if  (stat(tempname,  &st2)  ==  -­‐1)  {      349                  return  (-­‐1);      350          }      351          return  (st1.st_mMme  !=  st2.st_mMme);      352  }   execlp()が正常終了したら一時 ファイルを残して終了する      104                  switch  (pw_edit(0))  {      105                  case  -­‐1:      106                          pw_fini();      107                          err(1,  "pw_edit()");      108                  case  0:      109                          pw_fini();      110                          errx(0,  "no  changes  made");      111                  default:      112                          break;      113                  }   変更がない時   変更がある時   vipw.c   waitpid()でエラーだったりexeclp()で不 正終了していたらtempnameファイルは 無いのでstat()が-­‐1になりreturn(-­‐1)   EDITORの終了を失敗し たり,execlp()でEDITOR が不正終了した場合  
  15. 後述のアレ   pw_edit()のところで      execlp(editor,  basename(editor),  tempname,  (char  *)NULL);

                 ↓    execlp(“vipw”  “vipw”,  “pw.XXXXX”,  (char  *)NULL);     こうなって,結局のところ      #  vipw  pw.XXXXX     な感じになる  
  16. そういえば   •  vipwコマンドに”pw.XXXXX”引数は不正なので usage()    while  ((ch  =  getopt(argc,

     argv,  "d:"))  !=  -­‐1)      switch  (ch)  {   …      default:        usage();      }   #  env  EDITOR=vipw  vipw   usage:  vipw  [-­‐d  directory]   vipw:  pw_edit():  No  such  file  or  directory   つまりこうなる  
  17. EDITORが不正終了したので      327          for  (;;)

     {      328                  if  (waitpid(editpid,  &pstat,  WUNTRACED)  ==  -­‐1)  {      329                          if  (errno  ==  EINTR)      330                                  conMnue;      331                          unlink(tempname);      332                          editpid  =  -­‐1;      333                          break;      334                  }  else  if  (WIFSTOPPED(pstat))  {      335                          raise(WSTOPSIG(pstat));      336                  }  else  if  (WIFEXITED(pstat)  &&  WEXITSTATUS(pstat)  ==  0)  {      337                          editpid  =  -­‐1;      338                          break;      339                  }  else  {      340                          unlink(tempname);      341                          editpid  =  -­‐1;      342                          break;      343                  }      344          }   execlp()が不正終了したら一時 ファイルを削除して終了する   pw_edit()  
  18. pw_edit()を振り返って      345          sigacMon(SIGINT,  &sa_int,

     NULL);      346          sigacMon(SIGQUIT,  &sa_quit,  NULL);      347          sigprocmask(SIG_SETMASK,  &oldsigset,  NULL);      348          if  (stat(tempname,  &st2)  ==  -­‐1)  {      349                  return  (-­‐1);      350          }      351          return  (st1.st_mMme  !=  st2.st_mMme);      352  }      104                  switch  (pw_edit(0))  {      105                  case  -­‐1:      106                          pw_fini();      107                          err(1,  "pw_edit()");      108                  case  0:      109                          pw_fini();      110                          errx(0,  "no  changes  made");      111                  default:      112                          break;      113                  }   vipw.c   execlp()で不正終了していたら tempnameファイルは無いので   stat()が-­‐1になりreturn(-­‐1)   EDITORの終了を失敗し たり,execlp()でEDITOR が不正終了した場合   #  env  EDITOR=vipw  vipw   usage:  vipw  [-­‐d  directory]   vipw:  pw_edit():  No  such  file  or  directory  
  19. vipw.c  まとめ   オプション処理.-­‐dだけ   パスワードファイルの場所を探す   パスワードファイルをロックする   パスワードファイルと同じディレク

    トリに作業用ファイルを作って, 中身をコピーする   EDITORで指定したコマンドに作業用ファイルを渡したり,   コマンドが異常終了したら作業ファイルを消したり,   作業ファイルの更新時間を確認したり,   作業ファイルが消されてたら更新時間を確認できない のでエラーを返したり,   する.  
  20. まとめ   •  vipwは内部でexeclp()を使ってEDITORを実行し,引 数に作業用ファイルを渡す   •  1つ目のエラーはEDITOR=vipwの時にvipwコマンド が-­‐d以外の引数を許容しないエラー  

    •  2つ目のエラーは,1つ目のエラーに起因して作業用 ファイルを消し,その後の処理で作業用ファイルを 見に行こうとするが存在しないので出るエラー   #  env  EDITOR=vipw  vipw   usage:  vipw  [-­‐d  directory]   vipw:  pw_edit():  No  such  file  or  directory