env editor=vipw vipwしてみた

33
env EDITOR=vipw vipw してみた @yogata

Upload: ogatay

Post on 30-Jun-2015

583 views

Category:

Documents


2 download

TRANSCRIPT

Page 1: env EDITOR=vipw vipwしてみた

env  EDITOR=vipw  vipw  してみた  

@yogata  

Page 2: env EDITOR=vipw vipwしてみた

vipw  

•  ユーザ追加・削除・情報変更する  

•  vipw  is  神  

vipw   useradd/adduser  

メリット   Jviが使える   特に無い  

デメリット   特にない   Luserとaddのどっちが先かわからない  

Page 3: env EDITOR=vipw vipwしてみた

こんなにもすばらしいvipw  •  使いたくなってきたはず    •  でも……viは慣れてないし……  

env  EDITOR=XXX  vipw  

Page 4: env EDITOR=vipw vipwしてみた

vipw  と  EDITOR変数  •  env  EDITOR=nano  vipw  

Page 5: env EDITOR=vipw vipwしてみた

vipw  と  EDITOR変数  •  env  EDITOR=emacs  vipw  

Page 6: env EDITOR=vipw vipwしてみた

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

Page 7: env EDITOR=vipw vipwしてみた

やってみる  

#  env  EDITOR=vipw  vipw  

rootになるのを忘れずに  

Page 8: env EDITOR=vipw vipwしてみた

やってみる  

Page 9: env EDITOR=vipw vipwしてみた

調べる  •  FreeBSD  – /usr/src/usr.sbin/vipw/vipw.c  – /usr/src/lib/libuMl/pw_uMl.c  

– /usr/include/pwd.h  

– err周りはここらへん  •  /usr/src/lib/libc/gen/err.c  

Page 10: env EDITOR=vipw vipwしてみた

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()する  

Page 11: env EDITOR=vipw vipwしてみた

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()  ?  

Page 12: env EDITOR=vipw vipwしてみた

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()  ?  

パスワードファイルを再構築に失敗したら,再度ファイルを編集するか確認する  

Page 13: env EDITOR=vipw vipwしてみた

vipw.c  まとめ  

オプション処理.-­‐dだけ  

キニナル  

Page 14: env EDITOR=vipw vipwしてみた

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)  

Page 15: env EDITOR=vipw vipwしてみた

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)を使う  

Page 16: env EDITOR=vipw vipwしてみた

vipw.c  まとめ  

オプション処理.-­‐dだけ  

パスワードファイルの場所を探す  

Page 17: env EDITOR=vipw vipwしてみた

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ファイルを開いて  

Page 18: env EDITOR=vipw vipwしてみた

vipw.c  まとめ  

オプション処理.-­‐dだけ  

パスワードファイルの場所を探す  

パスワードファイルをロックする  

Page 19: env EDITOR=vipw vipwしてみた

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は乱数に置き換え)を作成.  

Page 20: env EDITOR=vipw vipwしてみた

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の中身を一時ファイルにコピー  

Page 21: env EDITOR=vipw vipwしてみた

vipw.c  まとめ  

オプション処理.-­‐dだけ  

パスワードファイルの場所を探す  

パスワードファイルをロックする  

パスワードファイルと同じディレクトリに作業用ファイルを作って,中身をコピーする  

Page 22: env EDITOR=vipw vipwしてみた

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を設定  

Page 23: env EDITOR=vipw vipwしてみた

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  

後述  

Page 24: env EDITOR=vipw vipwしてみた

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()が異常終了したらファイルを削除する  

Page 25: env EDITOR=vipw vipwしてみた

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が不正終了した場合  

Page 26: env EDITOR=vipw vipwしてみた

後述のアレ  

pw_edit()のところで    

 execlp(editor,  basename(editor),  tempname,  (char  *)NULL);              ↓    execlp(“vipw”  “vipw”,  “pw.XXXXX”,  (char  *)NULL);  

 こうなって,結局のところ    

 #  vipw  pw.XXXXX    な感じになる  

Page 27: env EDITOR=vipw vipwしてみた

そういえば  •  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  

つまりこうなる  

Page 28: env EDITOR=vipw vipwしてみた

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()  

Page 29: env EDITOR=vipw vipwしてみた

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  

Page 30: env EDITOR=vipw vipwしてみた

vipw.c  まとめ  

オプション処理.-­‐dだけ  

パスワードファイルの場所を探す  

パスワードファイルをロックする  

パスワードファイルと同じディレクトリに作業用ファイルを作って,中身をコピーする  

EDITORで指定したコマンドに作業用ファイルを渡したり,  コマンドが異常終了したら作業ファイルを消したり,  作業ファイルの更新時間を確認したり,  作業ファイルが消されてたら更新時間を確認できないのでエラーを返したり,  する.  

Page 31: env EDITOR=vipw vipwしてみた

(参考)  env  EDITOR=nano  vipwのとき  

pw.XXXXX  の部分  

Page 32: env EDITOR=vipw vipwしてみた

pw_mkdb()  •  省略  

Page 33: env EDITOR=vipw vipwしてみた

まとめ  •  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