gopass.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. /*
  2. gopass.c -- Authorization password management.
  3. gopass [--cipher md5|blowfish] [--file filename] [--password password] realm user roles...
  4. This file provides facilities for creating passwords in a route configuration file.
  5. It supports either MD5 or Blowfish ciphers.
  6. */
  7. /********************************* Includes ***********************************/
  8. #include "goahead.h"
  9. /********************************** Locals ************************************/
  10. #define MAX_PASS 64
  11. /********************************* Forwards ***********************************/
  12. static char *getPassword(void);
  13. static void printUsage(void);
  14. static int writeAuthFile(char *path);
  15. #if ME_WIN_LIKE || VXWORKS
  16. static char *getpass(char *prompt);
  17. #endif
  18. /*********************************** Code *************************************/
  19. int main(int argc, char *argv[])
  20. {
  21. WebsBuf buf;
  22. char *cipher, *password, *authFile, *username, *encodedPassword, *realm, *cp, *roles;
  23. int i, errflg, nextArg;
  24. username = 0;
  25. errflg = 0;
  26. password = 0;
  27. authFile = 0;
  28. cipher = "blowfish";
  29. for (i = 1; i < argc && !errflg; i++) {
  30. if (argv[i][0] != '-') {
  31. break;
  32. }
  33. for (cp = &argv[i][1]; *cp && !errflg; cp++) {
  34. if (*cp == '-') {
  35. cp++;
  36. }
  37. if (smatch(cp, "cipher")) {
  38. if (++i == argc) {
  39. errflg++;
  40. } else {
  41. cipher = argv[i];
  42. if (!smatch(cipher, "md5") && !smatch(cipher, "blowfish")) {
  43. error("Unknown cipher \"%s\". Use \"md5\" or \"blowfish\".", cipher);
  44. }
  45. break;
  46. }
  47. } else if (smatch(cp, "file") || smatch(cp, "f")) {
  48. if (++i == argc) {
  49. errflg++;
  50. } else {
  51. authFile = argv[i];
  52. break;
  53. }
  54. } else if (smatch(cp, "password") || smatch(cp, "p")) {
  55. if (++i == argc) {
  56. errflg++;
  57. } else {
  58. password = argv[i];
  59. break;
  60. }
  61. } else {
  62. errflg++;
  63. }
  64. }
  65. }
  66. nextArg = i;
  67. if ((nextArg + 3) > argc) {
  68. errflg++;
  69. }
  70. if (errflg) {
  71. printUsage();
  72. exit(2);
  73. }
  74. realm = argv[nextArg++];
  75. username = argv[nextArg++];
  76. bufCreate(&buf, 0, 0);
  77. for (i = nextArg; i < argc; ) {
  78. bufPutStr(&buf, argv[i]);
  79. if (++i < argc) {
  80. bufPutc(&buf, ',');
  81. }
  82. }
  83. roles = sclone(buf.servp);
  84. logOpen();
  85. websOpenAuth(1);
  86. if (authFile && access(authFile, R_OK) == 0) {
  87. if (websLoad(authFile) < 0) {
  88. exit(2);
  89. }
  90. if (access(authFile, W_OK) < 0) {
  91. error("Cannot write to %s", authFile);
  92. exit(4);
  93. }
  94. }
  95. if (!password && (password = getPassword()) == 0) {
  96. exit(1);
  97. }
  98. encodedPassword = websMD5(sfmt("%s:%s:%s", username, realm, password));
  99. if (smatch(cipher, "md5")) {
  100. encodedPassword = websMD5(sfmt("%s:%s:%s", username, realm, password));
  101. } else {
  102. /* This uses the more secure blowfish cipher */
  103. encodedPassword = websMakePassword(sfmt("%s:%s:%s", username, realm, password), 16, 128);
  104. }
  105. if (authFile) {
  106. websRemoveUser(username);
  107. if (websAddUser(username, encodedPassword, roles) == 0) {
  108. exit(7);
  109. }
  110. if (writeAuthFile(authFile) < 0) {
  111. exit(6);
  112. }
  113. } else {
  114. printf("%s\n", encodedPassword);
  115. }
  116. websCloseAuth();
  117. return 0;
  118. }
  119. static int writeAuthFile(char *path)
  120. {
  121. FILE *fp;
  122. WebsKey *kp, *ap;
  123. WebsRole *role;
  124. WebsUser *user;
  125. WebsHash roles, users;
  126. char *tempFile;
  127. assert(path && *path);
  128. tempFile = websTempFile(NULL, "gp");
  129. if ((fp = fopen(tempFile, "w" FILE_TEXT)) == 0) {
  130. error("Cannot open %s", tempFile);
  131. return -1;
  132. }
  133. fprintf(fp, "#\n# %s - Authorization data\n#\n\n", basename(path));
  134. roles = websGetRoles();
  135. if (roles >= 0) {
  136. for (kp = hashFirst(roles); kp; kp = hashNext(roles, kp)) {
  137. role = kp->content.value.symbol;
  138. fprintf(fp, "role name=%s abilities=", kp->name.value.string);
  139. for (ap = hashFirst(role->abilities); ap; ap = hashNext(role->abilities, ap)) {
  140. fprintf(fp, "%s,", ap->name.value.string);
  141. }
  142. fputc('\n', fp);
  143. }
  144. fputc('\n', fp);
  145. }
  146. users = websGetUsers();
  147. if (users >= 0) {
  148. for (kp = hashFirst(users); kp; kp = hashNext(users, kp)) {
  149. user = kp->content.value.symbol;
  150. fprintf(fp, "user name=%s password=%s roles=%s", user->name, user->password, user->roles);
  151. fputc('\n', fp);
  152. }
  153. }
  154. fclose(fp);
  155. unlink(path);
  156. if (rename(tempFile, path) < 0) {
  157. error("Cannot create new %s from %s errno %d", path, tempFile, errno);
  158. return -1;
  159. }
  160. return 0;
  161. }
  162. static char *getPassword(void)
  163. {
  164. char *password, *confirm;
  165. password = getpass("New password: ");
  166. confirm = getpass("Confirm password: ");
  167. if (smatch(password, confirm)) {
  168. return password;
  169. }
  170. error("Password not verified");
  171. return 0;
  172. }
  173. #if WINCE
  174. static char *getpass(char *prompt)
  175. {
  176. return "NOT-SUPPORTED";
  177. }
  178. #elif ME_WIN_LIKE || VXWORKS
  179. static char *getpass(char *prompt)
  180. {
  181. static char password[MAX_PASS];
  182. int c, i;
  183. fputs(prompt, stdout);
  184. for (i = 0; i < (int) sizeof(password) - 1; i++) {
  185. #if VXWORKS
  186. c = getchar();
  187. #else
  188. c = _getch();
  189. #endif
  190. if (c == '\r' || c == EOF) {
  191. break;
  192. }
  193. if ((c == '\b' || c == 127) && i > 0) {
  194. password[--i] = '\0';
  195. fputs("\b \b", stdout);
  196. i--;
  197. } else if (c == 26) { /* Control Z */
  198. c = EOF;
  199. break;
  200. } else if (c == 3) { /* Control C */
  201. fputs("^C\n", stdout);
  202. exit(255);
  203. } else if (!iscntrl((uchar) c) && (i < (int) sizeof(password) - 1)) {
  204. password[i] = c;
  205. fputc('*', stdout);
  206. } else {
  207. fputc('', stdout);
  208. i--;
  209. }
  210. }
  211. if (c == EOF) {
  212. return "";
  213. }
  214. fputc('\n', stdout);
  215. password[i] = '\0';
  216. return sclone(password);
  217. }
  218. #endif /* ME_WIN_LIKE */
  219. /*
  220. Display the usage
  221. */
  222. static void printUsage(void)
  223. {
  224. error("usage: gopass [--cipher cipher] [--file path] [--password password] realm user roles...\n"
  225. "Options:\n"
  226. " --cipher md5|blowfish Select the encryption cipher. Defaults to md5\n"
  227. " --file filename Modify the password file\n"
  228. " --password password Use the specified password\n"
  229. "\n");
  230. }
  231. /*
  232. Copyright (c) Embedthis Software. All Rights Reserved.
  233. This software is distributed under commercial and open source licenses.
  234. You may use the Embedthis GoAhead open source license or you may acquire
  235. a commercial license from Embedthis Software. You agree to be fully bound
  236. by the terms of either license. Consult the LICENSE.md distributed with
  237. this software for full details and other copyrights.
  238. */