ChipMaster's trial hacks on C++CMS starting with v1.2.1. Not sure I'll follow on with the v2 since it looks to be breaking and mostly frivolous.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

620 lines
13 KiB

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <sys/mman.h>
  4. #include <fcntl.h>
  5. #include <stddef.h>
  6. #include <unistd.h>
  7. #include <dirent.h>
  8. #include <errno.h>
  9. #include <stdio.h>
  10. #include "config.h"
  11. #ifdef CPPCMS_USE_EXTERNAL_BOOST
  12. # include <boost/crc.hpp>
  13. # include <boost/bind.hpp>
  14. #else // Internal Boost
  15. # include <cppcms_boost/crc.hpp>
  16. # include <cppcms_boost/bind.hpp>
  17. namespace boost = cppcms_boost;
  18. #endif
  19. #include <limits.h>
  20. #include "global_config.h"
  21. #include "session_file_storage.h"
  22. #include "cppcms_error.h"
  23. #include "session_sid.h"
  24. #include "config.h"
  25. using namespace std;
  26. namespace cppcms {
  27. namespace {
  28. bool flock(int fid,int how,int pos=-1)
  29. {
  30. struct flock lock;
  31. memset(&lock,0,sizeof(lock));
  32. lock.l_type=how;
  33. lock.l_whence=SEEK_SET;
  34. if(pos<0) { // All file
  35. lock.l_start=pos;
  36. lock.l_len=1;
  37. }
  38. int res;
  39. while((res=fcntl(fid,F_SETLKW,&lock)!=0) && errno==EINTR)
  40. ;
  41. if(res) return false;
  42. return true;
  43. }
  44. } // anon namespace
  45. namespace storage {
  46. #define LOCK_SIZE 256
  47. class io_error : public std::runtime_error {
  48. public:
  49. io_error() : std::runtime_error("IO"){};
  50. };
  51. void io::close(int fid) const
  52. {
  53. ::close(fid);
  54. }
  55. int io::lock_id(std::string const &sid) const
  56. {
  57. int id;
  58. char buf[3] = {0};
  59. buf[0]=sid.at(0);
  60. buf[1]=sid.at(1);
  61. sscanf(buf,"%x",&id);
  62. return id;
  63. }
  64. string io::mkname(std::string const &sid) const
  65. {
  66. return dir+"/"+sid;
  67. }
  68. void io::write(std::string const &sid,time_t timestamp,void const *buf,size_t len) const
  69. {
  70. string name=mkname(sid);
  71. int fid=::open(name.c_str(),O_CREAT | O_TRUNC | O_WRONLY ,0666);
  72. if(fid<0) {
  73. throw cppcms_error(errno,"storage::local_id");
  74. }
  75. ::write(fid,&timestamp,sizeof(time_t));
  76. ::write(fid,buf,len);
  77. boost::crc_32_type crc_calc;
  78. crc_calc.process_bytes(buf,len);
  79. uint32_t crc=crc_calc.checksum();
  80. ::write(fid,&crc,4);
  81. close(fid);
  82. }
  83. bool io::read(std::string const &sid,time_t &timestamp,vector<unsigned char> *out) const
  84. {
  85. int fid=-1;
  86. string name=mkname(sid);
  87. try {
  88. fid=::open(name.c_str(),O_RDONLY);
  89. if(fid<0) {
  90. return false;
  91. }
  92. time_t tmp;
  93. if(::read(fid,&tmp,sizeof(time_t))!=sizeof(time_t) || tmp < time(NULL))
  94. throw io_error();
  95. timestamp=tmp;
  96. if(!out) {
  97. close(fid);
  98. return true;
  99. }
  100. int size=lseek(fid,0,SEEK_END);
  101. if(size==-1 || size <(int)sizeof(time_t)+4)
  102. throw io_error();
  103. size-=sizeof(time_t)+4;
  104. if(lseek(fid,sizeof(time_t),SEEK_SET) < 0)
  105. throw io_error();
  106. out->resize(size,0);
  107. if(::read(fid,&out->front(),size)!=size)
  108. throw io_error();
  109. boost::crc_32_type crc_calc;
  110. crc_calc.process_bytes(&out->front(),size);
  111. uint32_t crc_ch,crc=crc_calc.checksum();
  112. if(::read(fid,&crc_ch,4)!=4 || crc_ch != crc)
  113. throw io_error();
  114. close(fid);
  115. return true;
  116. }
  117. catch(io_error const &e){
  118. if(fid>=0)
  119. close(fid);
  120. ::unlink(name.c_str());
  121. return false;
  122. }
  123. catch(...) {
  124. if(fid>=0) close(fid);
  125. ::unlink(name.c_str());
  126. throw;
  127. }
  128. }
  129. void io::unlink(std::string const &sid) const
  130. {
  131. string name=mkname(sid);
  132. ::unlink(name.c_str());
  133. }
  134. #if !defined(CPPCMS_EMBEDDED)
  135. local_io::local_io(std::string dir,pthread_rwlock_t *l):
  136. io(dir),
  137. locks(l)
  138. {
  139. }
  140. void local_io::wrlock(std::string const &sid) const
  141. {
  142. pthread_rwlock_wrlock(&locks[lock_id(sid)]);
  143. }
  144. void local_io::rdlock(std::string const &sid) const
  145. {
  146. pthread_rwlock_rdlock(&locks[lock_id(sid)]);
  147. }
  148. void local_io::unlock(std::string const &sid) const
  149. {
  150. pthread_rwlock_unlock(&locks[lock_id(sid)]);
  151. }
  152. void nfs_io::close(int fid) const
  153. {
  154. ::fsync(fid);
  155. ::close(fid);
  156. }
  157. nfs_io::nfs_io(std::string dir) : thread_io(dir)
  158. {
  159. string lockf=dir+"/"+"nfs.lock";
  160. fid=::open(lockf.c_str(),O_CREAT | O_RDWR,0666);
  161. if(fid<0) {
  162. throw cppcms_error(errno,"storage::nfs_io::open");
  163. }
  164. ::lseek(fid,LOCK_SIZE,SEEK_SET);
  165. ::write(fid,"",1);
  166. }
  167. nfs_io::~nfs_io()
  168. {
  169. ::close(fid);
  170. }
  171. // withing same process you should add additional mutex on the operations
  172. void nfs_io::wrlock(std::string const &sid) const
  173. {
  174. thread_io::wrlock(sid);
  175. if(!flock(fid,F_WRLCK,lock_id(sid)))
  176. throw cppcms_error(errno,"storage::nfs_io::fcntl::WRITE LOCK");
  177. }
  178. void nfs_io::rdlock(std::string const &sid) const
  179. {
  180. thread_io::rdlock(sid);
  181. if(!flock(fid,F_RDLCK,lock_id(sid)))
  182. throw cppcms_error(errno,"storage::nfs_io::fcntl::READ LOCK");
  183. }
  184. void nfs_io::unlock(std::string const &sid) const
  185. {
  186. flock(fid,F_UNLCK,lock_id(sid));
  187. thread_io::unlock(sid);
  188. }
  189. pthread_rwlock_t *thread_io::create_locks()
  190. {
  191. pthread_rwlock_t *array = new pthread_rwlock_t [LOCK_SIZE];
  192. for(int i=0;i<LOCK_SIZE;i++) {
  193. if(pthread_rwlock_init(array+i,NULL)) {
  194. int err=errno;
  195. i--;
  196. for(;i>=0;i--) {
  197. pthread_rwlock_destroy(array+i);
  198. }
  199. delete [] array;
  200. throw cppcms_error(err,"storage:pthread_rwlock_init");
  201. }
  202. }
  203. return array;
  204. }
  205. thread_io::thread_io(std::string dir) :
  206. local_io(dir,create_locks())
  207. {
  208. }
  209. thread_io::~thread_io()
  210. {
  211. for(int i=0;i<LOCK_SIZE;i++) {
  212. pthread_rwlock_destroy(locks+i);
  213. }
  214. delete [] locks;
  215. }
  216. #ifdef HAVE_PTHREADS_PSHARED
  217. pthread_rwlock_t *shmem_io::create_locks()
  218. {
  219. int size=sizeof(pthread_rwlock_t)*LOCK_SIZE;
  220. void *ptr=mmap(0,size,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANONYMOUS, -1,0);
  221. if(ptr==MAP_FAILED) {
  222. throw cppcms_error(errno,"storage:mmap");
  223. }
  224. pthread_rwlock_t *array =(pthread_rwlock_t*)ptr;
  225. for(int i=0;i<LOCK_SIZE;i++) {
  226. pthread_rwlockattr_t attr;
  227. if( pthread_rwlockattr_init(&attr) != 0
  228. || pthread_rwlockattr_setpshared(&attr,PTHREAD_PROCESS_SHARED) != 0
  229. || pthread_rwlock_init(array+i,&attr) !=0)
  230. {
  231. int err=errno;
  232. i--;
  233. for(;i>=0;i--) {
  234. pthread_rwlock_destroy(array+i);
  235. }
  236. munmap(ptr,size);
  237. throw cppcms_error(err,"storage:pthread_rwlock_init");
  238. }
  239. pthread_rwlockattr_destroy(&attr);
  240. }
  241. creator_pid=getpid();
  242. return array;
  243. }
  244. shmem_io::shmem_io(std::string dir) :
  245. local_io(dir,create_locks())
  246. {
  247. }
  248. shmem_io::~shmem_io()
  249. {
  250. if(creator_pid==getpid()) {
  251. for(int i=0;i<LOCK_SIZE;i++) {
  252. pthread_rwlock_destroy(locks+i);
  253. }
  254. }
  255. munmap(locks,sizeof(*locks)*LOCK_SIZE);
  256. }
  257. #endif // HAVE_PTHREADS_PSHARED
  258. #else // CPPCMS_EMBEDDED
  259. #if defined(CPPCMS_EMBEDDED_THREAD)
  260. static pthread_rwlock_t cppcms_embed_io_lock = PTHREAD_RWLOCK_INITIALIZER;
  261. #define embed_wrlock() pthread_rwlock_wrlock(&cppcms_embed_io_lock)
  262. #define embed_rdlock() pthread_rwlock_rdlock(&cppcms_embed_io_lock)
  263. #define embed_unlock() pthread_rwlock_unlock(&cppcms_embed_io_lock)
  264. #else
  265. #define embed_wrlock()
  266. #define embed_rdlock()
  267. #define embed_unlock()
  268. #endif
  269. embed_io::embed_io(std::string dir) : io(dir)
  270. {
  271. string lockf=dir+"/"+"flock";
  272. fid=::open(lockf.c_str(),O_CREAT | O_RDWR,0666);
  273. if(fid<0) {
  274. throw cppcms_error(errno,"storage::embed_io::open");
  275. }
  276. }
  277. embed_io::~embed_io()
  278. {
  279. ::close(fid);
  280. }
  281. void embed_io::wrlock(std::string const &sid) const
  282. {
  283. embed_wrlock();
  284. if(!flock(fid,F_WRLCK))
  285. throw cppcms_error(errno,"storage::nfs_io::fcntl::WRITE LOCK");
  286. }
  287. void embed_io::rdlock(std::string const &sid) const
  288. {
  289. embed_rdlock();
  290. if(!flock(fid,F_RDLCK))
  291. throw cppcms_error(errno,"storage::nfs_io::fcntl::READ LOCK");
  292. }
  293. void embed_io::unlock(std::string const &sid) const
  294. {
  295. flock(fid,F_UNLCK);
  296. embed_unlock();
  297. }
  298. #endif
  299. } // namespace storeage
  300. void session_file_storage::save(string const &sid,time_t timeout,string const &in)
  301. {
  302. try {
  303. io->wrlock(sid);
  304. io->write(sid,timeout,in.data(),in.size());
  305. io->unlock(sid);
  306. }
  307. catch(...) {
  308. io->unlock(sid);
  309. throw;
  310. }
  311. }
  312. bool session_file_storage::load(string const &sid,time_t *timeout,string &out)
  313. {
  314. try {
  315. io->rdlock(sid);
  316. vector<unsigned char> data;
  317. time_t tmp;
  318. bool res=io->read(sid,tmp,&data);
  319. io->unlock(sid);
  320. if(timeout) *timeout=tmp;
  321. out.assign(data.begin(),data.end());
  322. return res;
  323. }
  324. catch(...) {
  325. io->unlock(sid);
  326. throw;
  327. }
  328. }
  329. void session_file_storage::remove(string const &sid)
  330. {
  331. try {
  332. io->wrlock(sid);
  333. io->unlink(sid);
  334. io->unlock(sid);
  335. }
  336. catch(...) {
  337. io->unlock(sid);
  338. throw;
  339. }
  340. }
  341. void session_file_storage::gc(boost::shared_ptr<storage::io> io)
  342. {
  343. DIR *d=NULL;
  344. string dir=io->get_dir();
  345. struct dirent *entry_st=NULL,*entry_p;
  346. int path_len=pathconf(dir.c_str(),_PC_NAME_MAX);
  347. if(path_len < 0 ) {
  348. // Only "sessions" should be in this directory
  349. // also this directory has high level of trust
  350. // thus... Don't care about symlink exploits
  351. #ifdef NAME_MAX
  352. path_len=NAME_MAX;
  353. #elif defined(PATH_MAX)
  354. path_len=PATH_MAX;
  355. #else
  356. path_len=4096; // guess
  357. #endif
  358. }
  359. // this is for Solaris...
  360. entry_st=(struct dirent *)new char[sizeof(struct dirent)+path_len+1];
  361. try{
  362. if((d=opendir(dir.c_str()))==NULL) {
  363. int err=errno;
  364. throw cppcms_error(err,"Failed to open directory :"+dir);
  365. }
  366. while(readdir_r(d,entry_st,&entry_p)==0 && entry_p!=NULL) {
  367. int i;
  368. for(i=0;i<32;i++) {
  369. if(!isxdigit(entry_st->d_name[i]))
  370. break;
  371. }
  372. if(i!=32 || entry_st->d_name[i]!=0)
  373. continue;
  374. string sid=entry_st->d_name;
  375. try{
  376. io->rdlock(sid);
  377. time_t tmp;
  378. io->read(sid,tmp,NULL);
  379. // if it timeouted -- will be removed
  380. io->unlock(sid);
  381. }
  382. catch(...) {
  383. io->unlock(sid);
  384. throw;
  385. }
  386. }
  387. closedir(d);
  388. }
  389. catch(...) {
  390. if(d) closedir(d);
  391. delete [] entry_st;
  392. throw;
  393. }
  394. }
  395. #ifndef NO_BUILDER_INTERFACE
  396. namespace {
  397. #if !defined(CPPCMS_EMBEDDED) || defined(CPPCMS_EMBEDDED_THREAD)
  398. static pthread_mutex_t gc_mutex=PTHREAD_MUTEX_INITIALIZER;
  399. static pthread_cond_t gc_cond=PTHREAD_COND_INITIALIZER;
  400. static int gc_exit=-1;
  401. static int starter_pid=-1;
  402. #endif
  403. static int gc_period=-1;
  404. void *thread_func(void *param);
  405. struct builder_impl : private boost::noncopyable {
  406. public:
  407. boost::shared_ptr<storage::io> io;
  408. bool has_thread;
  409. pthread_t pid;
  410. int cache;
  411. string def_dir()
  412. {
  413. char const *d=getenv("TEMP");
  414. if(!d) d=getenv("TMP");
  415. if(!d) d="/tmp";
  416. string dir=string(d)+"/cppcms_sessions";
  417. if(::mkdir(dir.c_str(),0777)!=0 && errno!=EEXIST)
  418. throw cppcms_error(errno,"session::file_storage::mkdir");
  419. return dir;
  420. }
  421. builder_impl(cppcms_config const config)
  422. {
  423. string dir=config.sval("session.files_dir","");
  424. if(dir=="") {
  425. dir=def_dir();
  426. }
  427. #if !defined(CPPCMS_EMBEDDED)
  428. string mod = config.sval("server.mod","");
  429. string default_type;
  430. if(mod=="thread")
  431. default_type = "thread";
  432. #ifdef HAVE_PTHREADS_PSHARED
  433. else if(mod=="prefork")
  434. default_type = "prefork";
  435. #endif
  436. else
  437. default_type = "nfs";
  438. string type=config.sval("session.files_comp",default_type);
  439. if(type=="thread")
  440. io.reset(new storage::thread_io(dir));
  441. #ifdef HAVE_PTHREADS_PSHARED
  442. else if(type=="prefork")
  443. io.reset(new storage::shmem_io(dir));
  444. #endif
  445. else if(type=="nfs")
  446. io.reset(new storage::nfs_io(dir));
  447. else
  448. throw cppcms_error("Unknown option for session.files_comp `"+type+"'");
  449. #else // EMBEDDED
  450. io.reset(new storage::embed_io(dir));
  451. #endif
  452. gc_period=config.ival("session.files_gc_frequency",-1);
  453. #if !defined(CPPCMS_EMBEDDED) || defined(CPPCMS_EMBEDDED_THREAD)
  454. // Clean first time
  455. session_file_storage::gc(io);
  456. gc_exit=-1;
  457. if(gc_period>0) {
  458. has_thread=true;
  459. gc_exit=0;
  460. starter_pid=getpid();
  461. pthread_create(&pid,NULL,thread_func,this);
  462. }
  463. else
  464. has_thread=false;
  465. #else // We have only cgi
  466. string timestamp=dir+"/gc.timestamp";
  467. struct stat info;
  468. if(stat(timestamp.c_str(),&info)==0 && (time(NULL) - info.st_mtime) < gc_period)
  469. return;
  470. fclose(fopen(timestamp.c_str(),"w")); // timestamp it
  471. session_file_storage::gc(io);
  472. #endif
  473. }
  474. ~builder_impl()
  475. {
  476. #if !defined(CPPCMS_EMBEDDED) || defined(CPPCMS_EMBEDDED_THREAD)
  477. if(has_thread) {
  478. pthread_mutex_lock(&gc_mutex);
  479. gc_exit=1;
  480. pthread_cond_signal(&gc_cond);
  481. pthread_mutex_unlock(&gc_mutex);
  482. pthread_join(pid,NULL);
  483. }
  484. #endif
  485. }
  486. boost::shared_ptr<session_api> operator()(worker_thread &w)
  487. {
  488. boost::shared_ptr<session_server_storage> ss(new session_file_storage(io));
  489. return boost::shared_ptr<session_sid>(new session_sid(ss,cache));
  490. }
  491. };
  492. struct builder {
  493. boost::shared_ptr<builder_impl> the_builder;
  494. builder(boost::shared_ptr<builder_impl> b) :
  495. the_builder(b)
  496. {
  497. }
  498. boost::shared_ptr<session_api> operator()(worker_thread &w)
  499. {
  500. return (*the_builder)(w);
  501. }
  502. };
  503. void *thread_func(void *param)
  504. {
  505. #if !defined(CPPCMS_EMBEDDED) || defined(CPPCMS_EMBEDDED_THREAD)
  506. builder_impl *blder=(builder_impl*)param;
  507. int exit=0;
  508. while(!exit) {
  509. pthread_mutex_lock(&gc_mutex);
  510. struct timespec ts;
  511. clock_gettime(CLOCK_REALTIME, &ts);
  512. ts.tv_sec+=gc_period;
  513. pthread_cond_timedwait(&gc_cond,&gc_mutex,&ts);
  514. if(starter_pid!=getpid() || gc_exit) exit=1;
  515. pthread_mutex_unlock(&gc_mutex);
  516. if(!exit) {
  517. try {
  518. session_file_storage::gc(blder->io);
  519. }
  520. catch(std::exception const &e) {
  521. fprintf(stderr,"%s\n",e.what());
  522. return NULL;
  523. }
  524. catch(...)
  525. {
  526. return NULL;
  527. }
  528. }
  529. }
  530. #endif
  531. return NULL;
  532. }
  533. } // anon namespace
  534. session_backend_factory session_file_storage::factory(cppcms_config const &conf)
  535. {
  536. return builder(boost::shared_ptr<builder_impl>(new builder_impl(conf)));
  537. }
  538. #else // !NO_BUILDER_INTERFACE
  539. session_backend_factory session_file_storage::factory(cppcms_config const &conf)
  540. {
  541. throw runtime_error("session_file_storage::factory should not be used");
  542. }
  543. #endif
  544. } // namespace cppcms