PageRenderTime 42ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/src/ssfossil/fossil/src/user.c

https://github.com/paulfitz/coopy
C | 388 lines | 256 code | 22 blank | 110 comment | 63 complexity | da81f1ef3c6184185e0cd1d49c78c081 MD5 | raw file
  1. /*
  2. ** Copyright (c) 2006 D. Richard Hipp
  3. **
  4. ** This program is free software; you can redistribute it and/or
  5. ** modify it under the terms of the Simplified BSD License (also
  6. ** known as the "2-Clause License" or "FreeBSD License".)
  7. ** This program is distributed in the hope that it will be useful,
  8. ** but without any warranty; without even the implied warranty of
  9. ** merchantability or fitness for a particular purpose.
  10. **
  11. ** Author contact information:
  12. ** drh@hwaci.com
  13. ** http://www.hwaci.com/drh/
  14. **
  15. *******************************************************************************
  16. **
  17. ** Commands and procedures used for creating, processing, editing, and
  18. ** querying information about users.
  19. */
  20. #include "config.h"
  21. #include "user.h"
  22. /*
  23. ** Strip leading and trailing space from a string and add the string
  24. ** onto the end of a blob.
  25. */
  26. static void strip_string(Blob *pBlob, char *z){
  27. int i;
  28. blob_reset(pBlob);
  29. while( isspace(*z) ){ z++; }
  30. for(i=0; z[i]; i++){
  31. if( z[i]=='\r' || z[i]=='\n' ){
  32. while( i>0 && isspace(z[i-1]) ){ i--; }
  33. z[i] = 0;
  34. break;
  35. }
  36. if( z[i]<' ' ) z[i] = ' ';
  37. }
  38. blob_append(pBlob, z, -1);
  39. }
  40. #if defined(_WIN32)
  41. #ifdef __MINGW32__
  42. #include <conio.h>
  43. #endif
  44. /*
  45. ** getpass for Windows
  46. */
  47. static char *getpass(const char *prompt){
  48. static char pwd[64];
  49. size_t i;
  50. fputs(prompt,stderr);
  51. fflush(stderr);
  52. for(i=0; i<sizeof(pwd)-1; ++i){
  53. pwd[i] = _getch();
  54. if(pwd[i]=='\r' || pwd[i]=='\n'){
  55. break;
  56. }
  57. /* BS or DEL */
  58. else if(i>0 && (pwd[i]==8 || pwd[i]==127)){
  59. i -= 2;
  60. continue;
  61. }
  62. /* CTRL-C */
  63. else if(pwd[i]==3) {
  64. i=0;
  65. break;
  66. }
  67. /* ESC */
  68. else if(pwd[i]==27){
  69. i=0;
  70. break;
  71. }
  72. else{
  73. fputc('*',stderr);
  74. }
  75. }
  76. pwd[i]='\0';
  77. fputs("\n", stderr);
  78. return pwd;
  79. }
  80. #endif
  81. /*
  82. ** Do a single prompt for a passphrase. Store the results in the blob.
  83. */
  84. static void prompt_for_passphrase(const char *zPrompt, Blob *pPassphrase){
  85. char *z = getpass(zPrompt);
  86. strip_string(pPassphrase, z);
  87. }
  88. /*
  89. ** Prompt the user for a password. Store the result in the pPassphrase
  90. ** blob.
  91. **
  92. ** Behavior is controlled by the verify parameter:
  93. **
  94. ** 0 Just ask once.
  95. **
  96. ** 1 If the first answer is a non-empty string, ask for
  97. ** verification. Repeat if the two strings do not match.
  98. **
  99. ** 2 Ask twice, repeat if the strings do not match.
  100. */
  101. void prompt_for_password(
  102. const char *zPrompt,
  103. Blob *pPassphrase,
  104. int verify
  105. ){
  106. Blob secondTry;
  107. blob_zero(pPassphrase);
  108. blob_zero(&secondTry);
  109. while(1){
  110. prompt_for_passphrase(zPrompt, pPassphrase);
  111. if( verify==0 ) break;
  112. if( verify==1 && blob_size(pPassphrase)==0 ) break;
  113. prompt_for_passphrase("Again: ", &secondTry);
  114. if( blob_compare(pPassphrase, &secondTry) ){
  115. printf("Passphrases do not match. Try again...\n");
  116. }else{
  117. break;
  118. }
  119. }
  120. blob_reset(&secondTry);
  121. }
  122. /*
  123. ** Prompt the user to enter a single line of text.
  124. */
  125. void prompt_user(const char *zPrompt, Blob *pIn){
  126. char *z;
  127. char zLine[1000];
  128. blob_zero(pIn);
  129. printf("%s", zPrompt);
  130. fflush(stdout);
  131. z = fgets(zLine, sizeof(zLine), stdin);
  132. if( z ){
  133. strip_string(pIn, z);
  134. }
  135. }
  136. /*
  137. ** COMMAND: user
  138. **
  139. ** Usage: %fossil user SUBCOMMAND ... ?-R|--repository FILE?
  140. **
  141. ** Run various subcommands on users of the open repository or of
  142. ** the repository identified by the -R or --repository option.
  143. **
  144. ** %fossil user capabilities USERNAME ?STRING?
  145. **
  146. ** Query or set the capabilities for user USERNAME
  147. **
  148. ** %fossil user default ?USERNAME?
  149. **
  150. ** Query or set the default user. The default user is the
  151. ** user for command-line interaction.
  152. **
  153. ** %fossil user list
  154. **
  155. ** List all users known to the repository
  156. **
  157. ** %fossil user new ?USERNAME? ?CONTACT-INFO? ?PASSWORD?
  158. **
  159. ** Create a new user in the repository. Users can never be
  160. ** deleted. They can be denied all access but they must continue
  161. ** to exist in the database.
  162. **
  163. ** %fossil user password USERNAME ?PASSWORD?
  164. **
  165. ** Change the web access password for a user.
  166. */
  167. void user_cmd(void){
  168. int n;
  169. db_find_and_open_repository(1);
  170. if( g.argc<3 ){
  171. usage("capabilities|default|list|new|password ...");
  172. }
  173. n = strlen(g.argv[2]);
  174. if( n>=2 && strncmp(g.argv[2],"new",n)==0 ){
  175. Blob passwd, login, contact;
  176. char *zPw;
  177. if( g.argc>=4 ){
  178. blob_init(&login, g.argv[3], -1);
  179. }else{
  180. prompt_user("login: ", &login);
  181. }
  182. if( db_exists("SELECT 1 FROM user WHERE login=%B", &login) ){
  183. fossil_fatal("user %b already exists", &login);
  184. }
  185. if( g.argc>=5 ){
  186. blob_init(&contact, g.argv[4], -1);
  187. }else{
  188. prompt_user("contact-info: ", &contact);
  189. }
  190. if( g.argc>=6 ){
  191. blob_init(&passwd, g.argv[5], -1);
  192. }else{
  193. prompt_for_password("password: ", &passwd, 1);
  194. }
  195. zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login));
  196. db_multi_exec(
  197. "INSERT INTO user(login,pw,cap,info)"
  198. "VALUES(%B,%Q,'v',%B)",
  199. &login, zPw, &contact
  200. );
  201. free(zPw);
  202. }else if( n>=2 && strncmp(g.argv[2],"default",n)==0 ){
  203. user_select();
  204. if( g.argc==3 ){
  205. printf("%s\n", g.zLogin);
  206. }else{
  207. if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.argv[3]) ){
  208. fossil_fatal("no such user: %s", g.argv[3]);
  209. }
  210. if( g.localOpen ){
  211. db_lset("default-user", g.argv[3]);
  212. }else{
  213. db_set("default-user", g.argv[3], 0);
  214. }
  215. }
  216. }else if( n>=2 && strncmp(g.argv[2],"list",n)==0 ){
  217. Stmt q;
  218. db_prepare(&q, "SELECT login, info FROM user ORDER BY login");
  219. while( db_step(&q)==SQLITE_ROW ){
  220. printf("%-12s %s\n", db_column_text(&q, 0), db_column_text(&q, 1));
  221. }
  222. db_finalize(&q);
  223. }else if( n>=2 && strncmp(g.argv[2],"password",2)==0 ){
  224. char *zPrompt;
  225. int uid;
  226. Blob pw;
  227. if( g.argc!=4 && g.argc!=5 ) usage("password USERNAME ?NEW-PASSWORD?");
  228. uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", g.argv[3]);
  229. if( uid==0 ){
  230. fossil_fatal("no such user: %s", g.argv[3]);
  231. }
  232. if( g.argc==5 ){
  233. blob_init(&pw, g.argv[4], -1);
  234. }else{
  235. zPrompt = mprintf("new passwd for %s: ", g.argv[3]);
  236. prompt_for_password(zPrompt, &pw, 1);
  237. }
  238. if( blob_size(&pw)==0 ){
  239. printf("password unchanged\n");
  240. }else{
  241. char *zSecret = sha1_shared_secret(blob_str(&pw), g.argv[3]);
  242. db_multi_exec("UPDATE user SET pw=%Q WHERE uid=%d", zSecret, uid);
  243. free(zSecret);
  244. }
  245. }else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){
  246. int uid;
  247. if( g.argc!=4 && g.argc!=5 ){
  248. usage("user capabilities USERNAME ?PERMISSIONS?");
  249. }
  250. uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", g.argv[3]);
  251. if( uid==0 ){
  252. fossil_fatal("no such user: %s", g.argv[3]);
  253. }
  254. if( g.argc==5 ){
  255. db_multi_exec(
  256. "UPDATE user SET cap=%Q WHERE uid=%d", g.argv[4],
  257. uid
  258. );
  259. }
  260. printf("%s\n", db_text(0, "SELECT cap FROM user WHERE uid=%d", uid));
  261. }else{
  262. fossil_panic("user subcommand should be one of: "
  263. "capabilities default list new password");
  264. }
  265. }
  266. /*
  267. ** Attempt to set the user to zLogin
  268. */
  269. static int attempt_user(const char *zLogin){
  270. int uid;
  271. if( zLogin==0 ){
  272. return 0;
  273. }
  274. uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
  275. if( uid ){
  276. g.userUid = uid;
  277. g.zLogin = mprintf("%s", zLogin);
  278. return 1;
  279. }
  280. return 0;
  281. }
  282. /*
  283. ** Figure out what user is at the controls.
  284. **
  285. ** (1) Use the --user and -U command-line options.
  286. **
  287. ** (2) If the local database is open, check in VVAR.
  288. **
  289. ** (3) Check the default user in the repository
  290. **
  291. ** (4) Try the USER environment variable.
  292. **
  293. ** (5) Use the first user in the USER table.
  294. **
  295. ** The user name is stored in g.zLogin. The uid is in g.userUid.
  296. */
  297. void user_select(void){
  298. Stmt s;
  299. if( g.userUid ) return;
  300. if( attempt_user(g.zLogin) ) return;
  301. if( g.localOpen && attempt_user(db_lget("default-user",0)) ) return;
  302. if( attempt_user(db_get("default-user", 0)) ) return;
  303. if( attempt_user(getenv("USER")) ) return;
  304. db_prepare(&s,
  305. "SELECT uid, login FROM user"
  306. " WHERE login NOT IN ('anonymous','nobody','reader','developer')"
  307. );
  308. if( db_step(&s)==SQLITE_ROW ){
  309. g.userUid = db_column_int(&s, 0);
  310. g.zLogin = mprintf("%s", db_column_text(&s, 1));
  311. }
  312. db_finalize(&s);
  313. if( g.userUid==0 ){
  314. db_prepare(&s, "SELECT uid, login FROM user");
  315. if( db_step(&s)==SQLITE_ROW ){
  316. g.userUid = db_column_int(&s, 0);
  317. g.zLogin = mprintf("%s", db_column_text(&s, 1));
  318. }
  319. db_finalize(&s);
  320. }
  321. if( g.userUid==0 ){
  322. db_multi_exec(
  323. "INSERT INTO user(login, pw, cap, info)"
  324. "VALUES('anonymous', '', 'cfghjkmnoqw', '')"
  325. );
  326. g.userUid = db_last_insert_rowid();
  327. g.zLogin = "anonymous";
  328. }
  329. }
  330. /*
  331. ** Compute the shared secret for a user.
  332. */
  333. static void user_sha1_shared_secret_func(
  334. sqlite3_context *context,
  335. int argc,
  336. sqlite3_value **argv
  337. ){
  338. char *zPw;
  339. char *zLogin;
  340. assert( argc==2 );
  341. zPw = (char*)sqlite3_value_text(argv[0]);
  342. zLogin = (char*)sqlite3_value_text(argv[1]);
  343. if( zPw && zLogin ){
  344. sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin), -1, free);
  345. }
  346. }
  347. /*
  348. ** COMMAND: test-hash-passwords
  349. **
  350. ** Usage: %fossil test-hash-passwords REPOSITORY
  351. **
  352. ** Convert all local password storage to use a SHA1 hash of the password
  353. ** rather than cleartext. Passwords that are already stored as the SHA1
  354. ** has are unchanged.
  355. */
  356. void user_hash_passwords_cmd(void){
  357. if( g.argc!=3 ) usage("REPOSITORY");
  358. db_open_repository(g.argv[2]);
  359. sqlite3_create_function(g.db, "sha1_shared_secret", 2, SQLITE_UTF8, 0,
  360. user_sha1_shared_secret_func, 0, 0);
  361. db_multi_exec(
  362. "UPDATE user SET pw=sha1_shared_secret(pw,login)"
  363. " WHERE length(pw)>0 AND length(pw)!=40"
  364. );
  365. }