Fossil SCM
Fixed a bug in /json/user/save which could cause the fossil-internally-defined version of the 'name' param to be used, overwriting a user's name with 'user/save'. The name now _must_ come from the request payload, to avoid this ambiguity.
Commit
7e25293c5e72a078706771b2ad1ab4c35269c417
Parent
21b57d8c5d6e26b…
1 file changed
+39
-17
+39
-17
| --- src/json_user.c | ||
| +++ src/json_user.c | ||
| @@ -198,16 +198,17 @@ | ||
| 198 | 198 | char const tgtHasSetup = zCap && (NULL!=strchr(zCap, 's')); |
| 199 | 199 | char tgtHadSetup = 0; |
| 200 | 200 | Blob sql = empty_blob; |
| 201 | 201 | Stmt q = empty_Stmt; |
| 202 | 202 | |
| 203 | +#if 0 | |
| 203 | 204 | if(!g.perm.Admin && !g.perm.Setup && !g.perm.Password){ |
| 204 | 205 | return json_set_err( FSL_JSON_E_DENIED, |
| 205 | 206 | "Password change requires 'a', 's', " |
| 206 | 207 | "or 'p' permissions."); |
| 207 | 208 | } |
| 208 | - | |
| 209 | +#endif | |
| 209 | 210 | if(uid<=0 && (!zName||!*zName)){ |
| 210 | 211 | return json_set_err(FSL_JSON_E_MISSING_ARGS, |
| 211 | 212 | "One of 'uid' or 'name' is required."); |
| 212 | 213 | }else if(uid>0){ |
| 213 | 214 | zNameFree = db_text(NULL, "SELECT login FROM user WHERE uid=%d",uid); |
| @@ -278,33 +279,39 @@ | ||
| 278 | 279 | blob_append(&sql, "UPDATE user SET",-1 ); |
| 279 | 280 | blob_append(&sql, " mtime=cast(strftime('%s') AS INTEGER)", -1); |
| 280 | 281 | |
| 281 | 282 | if((uid>0) && zNameNew){ |
| 282 | 283 | /* Check for name change... */ |
| 283 | - if( (!g.perm.Admin && !g.perm.Setup) | |
| 284 | - && zNameNew && (zName != zNameNew) | |
| 285 | - && (0!=strcmp(zNameNew,zName))){ | |
| 286 | - json_set_err( FSL_JSON_E_DENIED, | |
| 287 | - "Modifying user names requires 'a' or 's' privileges."); | |
| 288 | - goto error; | |
| 289 | - } | |
| 290 | - forceLogout = cson_value_true() | |
| 284 | + if(0!=strcmp(zName,zNameNew)){ | |
| 285 | + if( (!g.perm.Admin && !g.perm.Setup) | |
| 286 | + && (zName != zNameNew)){ | |
| 287 | + json_set_err( FSL_JSON_E_DENIED, | |
| 288 | + "Modifying user names requires 'a' or 's' privileges."); | |
| 289 | + goto error; | |
| 290 | + } | |
| 291 | + forceLogout = cson_value_true() | |
| 291 | 292 | /* reminders: 1) does not allocate. |
| 292 | - 2) we do this because changing a name | |
| 293 | - invalidates any login token because the old name | |
| 294 | - is part of the token hash. | |
| 293 | + 2) we do this because changing a name | |
| 294 | + invalidates any login token because the old name | |
| 295 | + is part of the token hash. | |
| 295 | 296 | */; |
| 296 | - blob_appendf(&sql, ", login=%Q", zNameNew); | |
| 297 | - ++gotFields; | |
| 297 | + blob_appendf(&sql, ", login=%Q", zNameNew); | |
| 298 | + ++gotFields; | |
| 299 | + } | |
| 298 | 300 | } |
| 299 | 301 | |
| 300 | 302 | if( zCap ){ |
| 301 | 303 | blob_appendf(&sql, ", cap=%Q", zCap); |
| 302 | 304 | ++gotFields; |
| 303 | 305 | } |
| 304 | 306 | #define TRY_LOGIN_GROUP 0 /* login group support is not yet implemented. */ |
| 305 | - if( zPW ){ | |
| 307 | + if( zPW && *zPW ){ | |
| 308 | + if(!g.perm.Admin && !g.perm.Setup && !g.perm.Password){ | |
| 309 | + return json_set_err( FSL_JSON_E_DENIED, | |
| 310 | + "Password change requires 'a', 's', " | |
| 311 | + "or 'p' permissions."); | |
| 312 | + } | |
| 306 | 313 | #if !TRY_LOGIN_GROUP |
| 307 | 314 | char * zPWHash = NULL; |
| 308 | 315 | ++gotFields; |
| 309 | 316 | zPWHash = sha1_shared_secret(zPW, zNameNew ? zNameNew : zName, NULL); |
| 310 | 317 | blob_appendf(&sql, ", pw=%Q", zPWHash); |
| @@ -389,22 +396,37 @@ | ||
| 389 | 396 | static cson_value * json_user_save(){ |
| 390 | 397 | /* try to get user info from GET/CLI args and construct |
| 391 | 398 | a JSON form of it... */ |
| 392 | 399 | cson_object * u = cson_new_object(); |
| 393 | 400 | char const * str = NULL; |
| 401 | + cson_value * tmpV = NULL; | |
| 394 | 402 | char b = -1; |
| 395 | 403 | int i = -1; |
| 396 | 404 | int uid = -1; |
| 397 | 405 | cson_value * payload = NULL; |
| 398 | 406 | #define PROP(LK) str = json_find_option_cstr(LK,NULL,NULL); \ |
| 399 | 407 | if(str){ cson_object_set(u, LK, json_new_string(str)); } (void)0 |
| 400 | - PROP("name"); | |
| 401 | 408 | PROP("password"); |
| 402 | 409 | PROP("info"); |
| 403 | 410 | PROP("capabilities"); |
| 404 | 411 | #undef PROP |
| 405 | - | |
| 412 | + if(g.isHTTP){ | |
| 413 | + tmpV = json_req_payload_get("name"); | |
| 414 | + } | |
| 415 | + if(!tmpV && !g.isHTTP){ | |
| 416 | + /* only do this in CLI mode, to avoid the fossil-internal "name" | |
| 417 | + param from become a user's name. Been there, done that | |
| 418 | + (renamed my account to "user/save"). | |
| 419 | + */ | |
| 420 | + str = json_find_option_cstr("name",NULL,NULL); | |
| 421 | + if(str){ | |
| 422 | + tmpV = json_new_string(str); | |
| 423 | + } | |
| 424 | + } | |
| 425 | + if(tmpV){ | |
| 426 | + cson_object_set(u, "name", tmpV); | |
| 427 | + } | |
| 406 | 428 | #define PROP(LK,DFLT) b = json_find_option_bool(LK,NULL,NULL,DFLT); \ |
| 407 | 429 | if(DFLT!=b){ cson_object_set(u, LK, cson_value_new_bool(b)); } (void)0 |
| 408 | 430 | PROP("forceLogout",-1); |
| 409 | 431 | #undef PROP |
| 410 | 432 | |
| 411 | 433 |
| --- src/json_user.c | |
| +++ src/json_user.c | |
| @@ -198,16 +198,17 @@ | |
| 198 | char const tgtHasSetup = zCap && (NULL!=strchr(zCap, 's')); |
| 199 | char tgtHadSetup = 0; |
| 200 | Blob sql = empty_blob; |
| 201 | Stmt q = empty_Stmt; |
| 202 | |
| 203 | if(!g.perm.Admin && !g.perm.Setup && !g.perm.Password){ |
| 204 | return json_set_err( FSL_JSON_E_DENIED, |
| 205 | "Password change requires 'a', 's', " |
| 206 | "or 'p' permissions."); |
| 207 | } |
| 208 | |
| 209 | if(uid<=0 && (!zName||!*zName)){ |
| 210 | return json_set_err(FSL_JSON_E_MISSING_ARGS, |
| 211 | "One of 'uid' or 'name' is required."); |
| 212 | }else if(uid>0){ |
| 213 | zNameFree = db_text(NULL, "SELECT login FROM user WHERE uid=%d",uid); |
| @@ -278,33 +279,39 @@ | |
| 278 | blob_append(&sql, "UPDATE user SET",-1 ); |
| 279 | blob_append(&sql, " mtime=cast(strftime('%s') AS INTEGER)", -1); |
| 280 | |
| 281 | if((uid>0) && zNameNew){ |
| 282 | /* Check for name change... */ |
| 283 | if( (!g.perm.Admin && !g.perm.Setup) |
| 284 | && zNameNew && (zName != zNameNew) |
| 285 | && (0!=strcmp(zNameNew,zName))){ |
| 286 | json_set_err( FSL_JSON_E_DENIED, |
| 287 | "Modifying user names requires 'a' or 's' privileges."); |
| 288 | goto error; |
| 289 | } |
| 290 | forceLogout = cson_value_true() |
| 291 | /* reminders: 1) does not allocate. |
| 292 | 2) we do this because changing a name |
| 293 | invalidates any login token because the old name |
| 294 | is part of the token hash. |
| 295 | */; |
| 296 | blob_appendf(&sql, ", login=%Q", zNameNew); |
| 297 | ++gotFields; |
| 298 | } |
| 299 | |
| 300 | if( zCap ){ |
| 301 | blob_appendf(&sql, ", cap=%Q", zCap); |
| 302 | ++gotFields; |
| 303 | } |
| 304 | #define TRY_LOGIN_GROUP 0 /* login group support is not yet implemented. */ |
| 305 | if( zPW ){ |
| 306 | #if !TRY_LOGIN_GROUP |
| 307 | char * zPWHash = NULL; |
| 308 | ++gotFields; |
| 309 | zPWHash = sha1_shared_secret(zPW, zNameNew ? zNameNew : zName, NULL); |
| 310 | blob_appendf(&sql, ", pw=%Q", zPWHash); |
| @@ -389,22 +396,37 @@ | |
| 389 | static cson_value * json_user_save(){ |
| 390 | /* try to get user info from GET/CLI args and construct |
| 391 | a JSON form of it... */ |
| 392 | cson_object * u = cson_new_object(); |
| 393 | char const * str = NULL; |
| 394 | char b = -1; |
| 395 | int i = -1; |
| 396 | int uid = -1; |
| 397 | cson_value * payload = NULL; |
| 398 | #define PROP(LK) str = json_find_option_cstr(LK,NULL,NULL); \ |
| 399 | if(str){ cson_object_set(u, LK, json_new_string(str)); } (void)0 |
| 400 | PROP("name"); |
| 401 | PROP("password"); |
| 402 | PROP("info"); |
| 403 | PROP("capabilities"); |
| 404 | #undef PROP |
| 405 | |
| 406 | #define PROP(LK,DFLT) b = json_find_option_bool(LK,NULL,NULL,DFLT); \ |
| 407 | if(DFLT!=b){ cson_object_set(u, LK, cson_value_new_bool(b)); } (void)0 |
| 408 | PROP("forceLogout",-1); |
| 409 | #undef PROP |
| 410 | |
| 411 |
| --- src/json_user.c | |
| +++ src/json_user.c | |
| @@ -198,16 +198,17 @@ | |
| 198 | char const tgtHasSetup = zCap && (NULL!=strchr(zCap, 's')); |
| 199 | char tgtHadSetup = 0; |
| 200 | Blob sql = empty_blob; |
| 201 | Stmt q = empty_Stmt; |
| 202 | |
| 203 | #if 0 |
| 204 | if(!g.perm.Admin && !g.perm.Setup && !g.perm.Password){ |
| 205 | return json_set_err( FSL_JSON_E_DENIED, |
| 206 | "Password change requires 'a', 's', " |
| 207 | "or 'p' permissions."); |
| 208 | } |
| 209 | #endif |
| 210 | if(uid<=0 && (!zName||!*zName)){ |
| 211 | return json_set_err(FSL_JSON_E_MISSING_ARGS, |
| 212 | "One of 'uid' or 'name' is required."); |
| 213 | }else if(uid>0){ |
| 214 | zNameFree = db_text(NULL, "SELECT login FROM user WHERE uid=%d",uid); |
| @@ -278,33 +279,39 @@ | |
| 279 | blob_append(&sql, "UPDATE user SET",-1 ); |
| 280 | blob_append(&sql, " mtime=cast(strftime('%s') AS INTEGER)", -1); |
| 281 | |
| 282 | if((uid>0) && zNameNew){ |
| 283 | /* Check for name change... */ |
| 284 | if(0!=strcmp(zName,zNameNew)){ |
| 285 | if( (!g.perm.Admin && !g.perm.Setup) |
| 286 | && (zName != zNameNew)){ |
| 287 | json_set_err( FSL_JSON_E_DENIED, |
| 288 | "Modifying user names requires 'a' or 's' privileges."); |
| 289 | goto error; |
| 290 | } |
| 291 | forceLogout = cson_value_true() |
| 292 | /* reminders: 1) does not allocate. |
| 293 | 2) we do this because changing a name |
| 294 | invalidates any login token because the old name |
| 295 | is part of the token hash. |
| 296 | */; |
| 297 | blob_appendf(&sql, ", login=%Q", zNameNew); |
| 298 | ++gotFields; |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | if( zCap ){ |
| 303 | blob_appendf(&sql, ", cap=%Q", zCap); |
| 304 | ++gotFields; |
| 305 | } |
| 306 | #define TRY_LOGIN_GROUP 0 /* login group support is not yet implemented. */ |
| 307 | if( zPW && *zPW ){ |
| 308 | if(!g.perm.Admin && !g.perm.Setup && !g.perm.Password){ |
| 309 | return json_set_err( FSL_JSON_E_DENIED, |
| 310 | "Password change requires 'a', 's', " |
| 311 | "or 'p' permissions."); |
| 312 | } |
| 313 | #if !TRY_LOGIN_GROUP |
| 314 | char * zPWHash = NULL; |
| 315 | ++gotFields; |
| 316 | zPWHash = sha1_shared_secret(zPW, zNameNew ? zNameNew : zName, NULL); |
| 317 | blob_appendf(&sql, ", pw=%Q", zPWHash); |
| @@ -389,22 +396,37 @@ | |
| 396 | static cson_value * json_user_save(){ |
| 397 | /* try to get user info from GET/CLI args and construct |
| 398 | a JSON form of it... */ |
| 399 | cson_object * u = cson_new_object(); |
| 400 | char const * str = NULL; |
| 401 | cson_value * tmpV = NULL; |
| 402 | char b = -1; |
| 403 | int i = -1; |
| 404 | int uid = -1; |
| 405 | cson_value * payload = NULL; |
| 406 | #define PROP(LK) str = json_find_option_cstr(LK,NULL,NULL); \ |
| 407 | if(str){ cson_object_set(u, LK, json_new_string(str)); } (void)0 |
| 408 | PROP("password"); |
| 409 | PROP("info"); |
| 410 | PROP("capabilities"); |
| 411 | #undef PROP |
| 412 | if(g.isHTTP){ |
| 413 | tmpV = json_req_payload_get("name"); |
| 414 | } |
| 415 | if(!tmpV && !g.isHTTP){ |
| 416 | /* only do this in CLI mode, to avoid the fossil-internal "name" |
| 417 | param from become a user's name. Been there, done that |
| 418 | (renamed my account to "user/save"). |
| 419 | */ |
| 420 | str = json_find_option_cstr("name",NULL,NULL); |
| 421 | if(str){ |
| 422 | tmpV = json_new_string(str); |
| 423 | } |
| 424 | } |
| 425 | if(tmpV){ |
| 426 | cson_object_set(u, "name", tmpV); |
| 427 | } |
| 428 | #define PROP(LK,DFLT) b = json_find_option_bool(LK,NULL,NULL,DFLT); \ |
| 429 | if(DFLT!=b){ cson_object_set(u, LK, cson_value_new_bool(b)); } (void)0 |
| 430 | PROP("forceLogout",-1); |
| 431 | #undef PROP |
| 432 | |
| 433 |