Fossil SCM
If the FOSSIL_SECURITY_LEVEL environment variable is 2 or more, then present a simple substitution matrix when entering passwords, as a defense against key loggers. For FOSSIL_SECURITY_LEVEL of 1 or more, do not remember the remote-url password.
Commit
e1034c4c35195ef61602d4ec05a72e8681de6a98
Parent
fcfaae37dcbf82a…
1 file changed
+77
+77
| --- src/user.c | ||
| +++ src/user.c | ||
| @@ -97,10 +97,79 @@ | ||
| 97 | 97 | # undef popen |
| 98 | 98 | # define popen _popen |
| 99 | 99 | # undef pclose |
| 100 | 100 | # define pclose _pclose |
| 101 | 101 | #endif |
| 102 | + | |
| 103 | +/* | |
| 104 | +** Scramble substitution matrix: | |
| 105 | +*/ | |
| 106 | +static char aSubst[256]; | |
| 107 | + | |
| 108 | +/* | |
| 109 | +** Descramble the password | |
| 110 | +*/ | |
| 111 | +static void userDescramble(char *z){ | |
| 112 | + int i; | |
| 113 | + for(i=0; z[i]; i++) z[i] = aSubst[(unsigned char)z[i]]; | |
| 114 | +} | |
| 115 | + | |
| 116 | +/* Print a string in 5-letter groups */ | |
| 117 | +static void printFive(const unsigned char *z){ | |
| 118 | + int i; | |
| 119 | + for(i=0; z[i]; i++){ | |
| 120 | + if( i>0 && (i%5)==0 ) putchar(' '); | |
| 121 | + putchar(z[i]); | |
| 122 | + } | |
| 123 | + putchar('\n'); | |
| 124 | +} | |
| 125 | + | |
| 126 | +/* Return a pseudo-random integer between 0 and N-1 */ | |
| 127 | +static int randint(int N){ | |
| 128 | + unsigned char x; | |
| 129 | + assert( N<256 ); | |
| 130 | + sqlite3_randomness(1, &x); | |
| 131 | + return x % N; | |
| 132 | +} | |
| 133 | + | |
| 134 | +/* | |
| 135 | +** Generate and print a random scrambling of letters a through z (omitting x) | |
| 136 | +** and set up the aSubst[] matrix to descramble. | |
| 137 | +*/ | |
| 138 | +static void userGenerateScrambleCode(void){ | |
| 139 | + unsigned char zOrig[30]; | |
| 140 | + unsigned char zA[30]; | |
| 141 | + unsigned char zB[30]; | |
| 142 | + int nA = 25; | |
| 143 | + int nB = 0; | |
| 144 | + int i; | |
| 145 | + memcpy(zOrig, "abcdefghijklmnopqrstuvwyz", nA+1); | |
| 146 | + memcpy(zA, zOrig, nA+1); | |
| 147 | + assert( nA==(int)strlen((char*)zA) ); | |
| 148 | + for(i=0; i<sizeof(aSubst); i++) aSubst[i] = i; | |
| 149 | + printFive(zA); | |
| 150 | + while( nA>0 ){ | |
| 151 | + int x = randint(nA); | |
| 152 | + zB[nB++] = zA[x]; | |
| 153 | + zA[x] = zA[--nA]; | |
| 154 | + } | |
| 155 | + assert( nB==25 ); | |
| 156 | + zB[nB] = 0; | |
| 157 | + printFive(zB); | |
| 158 | + for(i=0; i<nB; i++) aSubst[zB[i]] = zOrig[i]; | |
| 159 | +} | |
| 160 | + | |
| 161 | +/* | |
| 162 | +** Return the value of the FOSSIL_SECURITY_LEVEL environment variable. | |
| 163 | +** Or return 0 if that variable does not exist. | |
| 164 | +*/ | |
| 165 | +int fossil_security_level(void){ | |
| 166 | + const char *zLevel = fossil_getenv("FOSSIL_SECURITY_LEVEL"); | |
| 167 | + if( zLevel==0 ) return 0; | |
| 168 | + return atoi(zLevel); | |
| 169 | +} | |
| 170 | + | |
| 102 | 171 | |
| 103 | 172 | /* |
| 104 | 173 | ** Do a single prompt for a passphrase. Store the results in the blob. |
| 105 | 174 | ** |
| 106 | 175 | ** If the FOSSIL_PWREADER environment variable is set, then it will |
| @@ -117,10 +186,11 @@ | ||
| 117 | 186 | ** on subsequent calls to this same routine. |
| 118 | 187 | */ |
| 119 | 188 | static void prompt_for_passphrase(const char *zPrompt, Blob *pPassphrase){ |
| 120 | 189 | char *z; |
| 121 | 190 | const char *zProg = fossil_getenv("FOSSIL_PWREADER"); |
| 191 | + const char *zSecure; | |
| 122 | 192 | if( zProg && zProg[0] ){ |
| 123 | 193 | static char zPass[100]; |
| 124 | 194 | Blob cmd; |
| 125 | 195 | FILE *in; |
| 126 | 196 | blob_zero(&cmd); |
| @@ -129,10 +199,16 @@ | ||
| 129 | 199 | in = popen(blob_str(&cmd), "r"); |
| 130 | 200 | fgets(zPass, sizeof(zPass), in); |
| 131 | 201 | pclose(in); |
| 132 | 202 | blob_reset(&cmd); |
| 133 | 203 | z = zPass; |
| 204 | + }else if( fossil_security_level()>=2 ){ | |
| 205 | + userGenerateScrambleCode(); | |
| 206 | + z = getpass(zPrompt); | |
| 207 | + if( z ) userDescramble(z); | |
| 208 | + printf("\033[3A\033[J"); /* Erase previous three lines */ | |
| 209 | + fflush(stdout); | |
| 134 | 210 | }else{ |
| 135 | 211 | z = getpass(zPrompt); |
| 136 | 212 | } |
| 137 | 213 | strip_string(pPassphrase, z); |
| 138 | 214 | } |
| @@ -180,10 +256,11 @@ | ||
| 180 | 256 | char c; |
| 181 | 257 | const char *old = db_get("last-sync-pw", 0); |
| 182 | 258 | if( (old!=0) && fossil_strcmp(unobscure(old), passwd)==0 ){ |
| 183 | 259 | return 0; |
| 184 | 260 | } |
| 261 | + if( fossil_security_level()>=1 ) return 0; | |
| 185 | 262 | prompt_user("remember password (Y/n)? ", &x); |
| 186 | 263 | c = blob_str(&x)[0]; |
| 187 | 264 | blob_reset(&x); |
| 188 | 265 | return ( c!='n' && c!='N' ); |
| 189 | 266 | } |
| 190 | 267 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -97,10 +97,79 @@ | |
| 97 | # undef popen |
| 98 | # define popen _popen |
| 99 | # undef pclose |
| 100 | # define pclose _pclose |
| 101 | #endif |
| 102 | |
| 103 | /* |
| 104 | ** Do a single prompt for a passphrase. Store the results in the blob. |
| 105 | ** |
| 106 | ** If the FOSSIL_PWREADER environment variable is set, then it will |
| @@ -117,10 +186,11 @@ | |
| 117 | ** on subsequent calls to this same routine. |
| 118 | */ |
| 119 | static void prompt_for_passphrase(const char *zPrompt, Blob *pPassphrase){ |
| 120 | char *z; |
| 121 | const char *zProg = fossil_getenv("FOSSIL_PWREADER"); |
| 122 | if( zProg && zProg[0] ){ |
| 123 | static char zPass[100]; |
| 124 | Blob cmd; |
| 125 | FILE *in; |
| 126 | blob_zero(&cmd); |
| @@ -129,10 +199,16 @@ | |
| 129 | in = popen(blob_str(&cmd), "r"); |
| 130 | fgets(zPass, sizeof(zPass), in); |
| 131 | pclose(in); |
| 132 | blob_reset(&cmd); |
| 133 | z = zPass; |
| 134 | }else{ |
| 135 | z = getpass(zPrompt); |
| 136 | } |
| 137 | strip_string(pPassphrase, z); |
| 138 | } |
| @@ -180,10 +256,11 @@ | |
| 180 | char c; |
| 181 | const char *old = db_get("last-sync-pw", 0); |
| 182 | if( (old!=0) && fossil_strcmp(unobscure(old), passwd)==0 ){ |
| 183 | return 0; |
| 184 | } |
| 185 | prompt_user("remember password (Y/n)? ", &x); |
| 186 | c = blob_str(&x)[0]; |
| 187 | blob_reset(&x); |
| 188 | return ( c!='n' && c!='N' ); |
| 189 | } |
| 190 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -97,10 +97,79 @@ | |
| 97 | # undef popen |
| 98 | # define popen _popen |
| 99 | # undef pclose |
| 100 | # define pclose _pclose |
| 101 | #endif |
| 102 | |
| 103 | /* |
| 104 | ** Scramble substitution matrix: |
| 105 | */ |
| 106 | static char aSubst[256]; |
| 107 | |
| 108 | /* |
| 109 | ** Descramble the password |
| 110 | */ |
| 111 | static void userDescramble(char *z){ |
| 112 | int i; |
| 113 | for(i=0; z[i]; i++) z[i] = aSubst[(unsigned char)z[i]]; |
| 114 | } |
| 115 | |
| 116 | /* Print a string in 5-letter groups */ |
| 117 | static void printFive(const unsigned char *z){ |
| 118 | int i; |
| 119 | for(i=0; z[i]; i++){ |
| 120 | if( i>0 && (i%5)==0 ) putchar(' '); |
| 121 | putchar(z[i]); |
| 122 | } |
| 123 | putchar('\n'); |
| 124 | } |
| 125 | |
| 126 | /* Return a pseudo-random integer between 0 and N-1 */ |
| 127 | static int randint(int N){ |
| 128 | unsigned char x; |
| 129 | assert( N<256 ); |
| 130 | sqlite3_randomness(1, &x); |
| 131 | return x % N; |
| 132 | } |
| 133 | |
| 134 | /* |
| 135 | ** Generate and print a random scrambling of letters a through z (omitting x) |
| 136 | ** and set up the aSubst[] matrix to descramble. |
| 137 | */ |
| 138 | static void userGenerateScrambleCode(void){ |
| 139 | unsigned char zOrig[30]; |
| 140 | unsigned char zA[30]; |
| 141 | unsigned char zB[30]; |
| 142 | int nA = 25; |
| 143 | int nB = 0; |
| 144 | int i; |
| 145 | memcpy(zOrig, "abcdefghijklmnopqrstuvwyz", nA+1); |
| 146 | memcpy(zA, zOrig, nA+1); |
| 147 | assert( nA==(int)strlen((char*)zA) ); |
| 148 | for(i=0; i<sizeof(aSubst); i++) aSubst[i] = i; |
| 149 | printFive(zA); |
| 150 | while( nA>0 ){ |
| 151 | int x = randint(nA); |
| 152 | zB[nB++] = zA[x]; |
| 153 | zA[x] = zA[--nA]; |
| 154 | } |
| 155 | assert( nB==25 ); |
| 156 | zB[nB] = 0; |
| 157 | printFive(zB); |
| 158 | for(i=0; i<nB; i++) aSubst[zB[i]] = zOrig[i]; |
| 159 | } |
| 160 | |
| 161 | /* |
| 162 | ** Return the value of the FOSSIL_SECURITY_LEVEL environment variable. |
| 163 | ** Or return 0 if that variable does not exist. |
| 164 | */ |
| 165 | int fossil_security_level(void){ |
| 166 | const char *zLevel = fossil_getenv("FOSSIL_SECURITY_LEVEL"); |
| 167 | if( zLevel==0 ) return 0; |
| 168 | return atoi(zLevel); |
| 169 | } |
| 170 | |
| 171 | |
| 172 | /* |
| 173 | ** Do a single prompt for a passphrase. Store the results in the blob. |
| 174 | ** |
| 175 | ** If the FOSSIL_PWREADER environment variable is set, then it will |
| @@ -117,10 +186,11 @@ | |
| 186 | ** on subsequent calls to this same routine. |
| 187 | */ |
| 188 | static void prompt_for_passphrase(const char *zPrompt, Blob *pPassphrase){ |
| 189 | char *z; |
| 190 | const char *zProg = fossil_getenv("FOSSIL_PWREADER"); |
| 191 | const char *zSecure; |
| 192 | if( zProg && zProg[0] ){ |
| 193 | static char zPass[100]; |
| 194 | Blob cmd; |
| 195 | FILE *in; |
| 196 | blob_zero(&cmd); |
| @@ -129,10 +199,16 @@ | |
| 199 | in = popen(blob_str(&cmd), "r"); |
| 200 | fgets(zPass, sizeof(zPass), in); |
| 201 | pclose(in); |
| 202 | blob_reset(&cmd); |
| 203 | z = zPass; |
| 204 | }else if( fossil_security_level()>=2 ){ |
| 205 | userGenerateScrambleCode(); |
| 206 | z = getpass(zPrompt); |
| 207 | if( z ) userDescramble(z); |
| 208 | printf("\033[3A\033[J"); /* Erase previous three lines */ |
| 209 | fflush(stdout); |
| 210 | }else{ |
| 211 | z = getpass(zPrompt); |
| 212 | } |
| 213 | strip_string(pPassphrase, z); |
| 214 | } |
| @@ -180,10 +256,11 @@ | |
| 256 | char c; |
| 257 | const char *old = db_get("last-sync-pw", 0); |
| 258 | if( (old!=0) && fossil_strcmp(unobscure(old), passwd)==0 ){ |
| 259 | return 0; |
| 260 | } |
| 261 | if( fossil_security_level()>=1 ) return 0; |
| 262 | prompt_user("remember password (Y/n)? ", &x); |
| 263 | c = blob_str(&x)[0]; |
| 264 | blob_reset(&x); |
| 265 | return ( c!='n' && c!='N' ); |
| 266 | } |
| 267 |