diff -Nru nss-passwords-0.3/debian/changelog nss-passwords-0.4/debian/changelog --- nss-passwords-0.3/debian/changelog 2022-01-01 09:08:23.000000000 +0000 +++ nss-passwords-0.4/debian/changelog 2023-09-19 14:30:05.000000000 +0000 @@ -1,3 +1,10 @@ +nss-passwords (0.4-1) unstable; urgency=medium + + * New upstream release (Closes: #1052233) + * Bump Standards-Version to 4.6.2 + + -- Stéphane Glondu Tue, 19 Sep 2023 16:30:05 +0200 + nss-passwords (0.3-2) unstable; urgency=medium [ Stéphane Glondu ] diff -Nru nss-passwords-0.3/debian/control nss-passwords-0.4/debian/control --- nss-passwords-0.3/debian/control 2022-01-01 09:08:23.000000000 +0000 +++ nss-passwords-0.4/debian/control 2023-09-19 14:30:05.000000000 +0000 @@ -14,7 +14,7 @@ libnspr4-dev, libnss3-dev, ocaml -Standards-Version: 4.6.0 +Standards-Version: 4.6.2 Rules-Requires-Root: no Homepage: https://github.com/glondu/nss-passwords Vcs-Git: https://salsa.debian.org/ocaml-team/nss-passwords.git diff -Nru nss-passwords-0.3/json_types.atd nss-passwords-0.4/json_types.atd --- nss-passwords-0.3/json_types.atd 2019-07-25 08:34:20.000000000 +0000 +++ nss-passwords-0.4/json_types.atd 2023-09-19 14:18:54.000000000 +0000 @@ -2,8 +2,16 @@ hostname : string; encryptedUsername : string; encryptedPassword : string; -} +} type logins = { logins : login list; } + +type output_login = { + hostname : string; + username : string; + password : string; +} + +type output = output_login list diff -Nru nss-passwords-0.3/main.ml nss-passwords-0.4/main.ml --- nss-passwords-0.3/main.ml 2019-07-25 08:34:20.000000000 +0000 +++ nss-passwords-0.4/main.ml 2023-09-19 14:18:54.000000000 +0000 @@ -30,6 +30,8 @@ * * ***** END LICENSE BLOCK ***** *) +open Json_types_j + (** C interface *) exception NSS_init_failed @@ -56,17 +58,37 @@ let dir = ref "" let pinentry = ref "pinentry" let queries = ref [] +let json_output = ref false let spec = Arg.align [ "-d", Arg.Set_string dir, "profile directory (default: Firefox default profile)"; "-p", Arg.Set_string pinentry, "pinentry program to use (default: pinentry)"; + "-j", Arg.Set json_output, " output result in JSON"; ] -let usage_msg = "nss-passwords [-d ] [-p ] query [...]" +let usage_msg = "\ +nss-passwords [-d ] [-p ] query [...]\n\ +A query is either hostname:string, username:string, or string (which\n\ +translates to hostname:string)." exception Found of string +let chop_prefix ~prefix x = + let n = String.length x and nprefix = String.length prefix in + if n >= nprefix && String.sub x 0 nprefix = prefix then + Some (String.sub x nprefix (n - nprefix)) + else + None + +let parse_query x = + match chop_prefix ~prefix:"hostname:" x with + | Some x -> `Hostname x + | None -> + match chop_prefix ~prefix:"username:" x with + | Some x -> `Username x + | None -> `Hostname x + let () = - Arg.parse spec (fun x -> queries := x :: !queries) usage_msg; + Arg.parse spec (fun x -> queries := parse_query x :: !queries) usage_msg; if !queries = [] then begin Arg.usage spec usage_msg; exit 1 @@ -147,38 +169,64 @@ let results = ref [] -let process_row = function - | [| hostname; encryptedUsername; encryptedPassword |] -> - let username = do_decrypt ~callback ~data:encryptedUsername in - let password = do_decrypt ~callback ~data:encryptedPassword in - results := (hostname, username, password) :: !results - | _ -> assert false - -let exec db query = +let exec_hostname db query = + let cb = function + | [| hostname; encryptedUsername; encryptedPassword |] -> + let username = do_decrypt ~callback ~data:encryptedUsername in + let password = do_decrypt ~callback ~data:encryptedPassword in + results := {hostname; username; password} :: !results + | _ -> assert false + in let buf = Buffer.create (2 * String.length query + 128) in Printf.bprintf buf "SELECT hostname, encryptedUsername, encryptedPassword FROM moz_logins WHERE hostname LIKE %a ESCAPE 'x';" quote_query query; - let r = Sqlite3.exec_not_null_no_headers ~cb:process_row db (Buffer.contents buf) in + let r = Sqlite3.exec_not_null_no_headers ~cb db (Buffer.contents buf) in assert (r = Sqlite3.Rc.OK) +let exec_username db query = + let rex = Str.regexp (".*" ^ Str.quote query ^ ".*") in + let cb = function + | [| hostname; encryptedUsername; encryptedPassword |] -> + let username = do_decrypt ~callback ~data:encryptedUsername in + if Str.string_match rex username 0 then ( + let password = do_decrypt ~callback ~data:encryptedPassword in + results := {hostname; username; password} :: !results + ) + | _ -> assert false + in + let sql = "SELECT hostname, encryptedUsername, encryptedPassword FROM moz_logins;" in + let r = Sqlite3.exec_not_null_no_headers ~cb db sql in + assert (r = Sqlite3.Rc.OK) + +let exec db = function + | `Hostname x -> exec_hostname db x + | `Username x -> exec_username db x + let exec_sqlite () = let db = Sqlite3.db_open (FilePath.concat !dir "signons.sqlite") in List.iter (exec db) !queries; let r = Sqlite3.db_close db in assert (r = true) -open Json_types_j - let json_process logins query = - let rex = Str.regexp (".*" ^ Str.quote query ^ ".*") in + let string_match = + match query with + | `Hostname x -> + let rex = Str.regexp (".*" ^ Str.quote x ^ ".*") in + fun hostname _ -> Str.string_match rex hostname 0 + | `Username x -> + let rex = Str.regexp (".*" ^ Str.quote x ^ ".*") in + fun _ username -> Str.string_match rex username 0 + in List.iter (fun l -> - if Str.string_match rex l.hostname 0 then ( - let username = do_decrypt ~callback ~data:l.encryptedUsername in - let password = do_decrypt ~callback ~data:l.encryptedPassword in - results := (l.hostname, username, password) :: !results - ) + let hostname = l.ihostname in + let username = do_decrypt ~callback ~data:l.iencryptedUsername in + if string_match hostname username then ( + let password = do_decrypt ~callback ~data:l.iencryptedPassword in + results := {hostname; username; password} :: !results + ) ) logins let exec_json () = @@ -189,6 +237,25 @@ close_in ic; List.iter (json_process logins.logins) !queries +let print_as_table results = + let (a, b, c) = + List.fold_left + (fun (a, b, c) o -> + let a = max a (String.length o.hostname) in + let b = max b (String.length o.username) in + let c = max c (String.length o.password) in + (a, b, c)) + (0, 0, 0) + results + in + List.iter + (fun o -> + Printf.printf "| %-*s | %-*s | %-*s |\n" a o.hostname b o.username c o.password + ) results + +let print_as_json results = + print_endline (string_of_output results) + let () = try (if Sys.file_exists (FilePath.concat !dir "logins.json") @@ -196,18 +263,7 @@ else exec_sqlite () ); let results = List.sort compare !results in - let (a, b, c) = List.fold_left - (fun (a, b, c) (x, y, z) -> - let a = max a (String.length x) in - let b = max b (String.length y) in - let c = max c (String.length z) in - (a, b, c)) - (0, 0, 0) - results - in - List.iter (fun (x, y, z) -> - Printf.printf "| %-*s | %-*s | %-*s |\n" a x b y c z - ) results + (if !json_output then print_as_json else print_as_table) results with | NSS_decrypt_failed(_, _, Some e) -> Printf.eprintf "Error while decrypting: %s\n" (Printexc.to_string e); diff -Nru nss-passwords-0.3/nss_stubs.c nss-passwords-0.4/nss_stubs.c --- nss-passwords-0.3/nss_stubs.c 2019-07-25 08:34:20.000000000 +0000 +++ nss-passwords-0.4/nss_stubs.c 2023-09-19 14:18:54.000000000 +0000 @@ -95,10 +95,10 @@ CAMLreturn(Val_unit); } -CAMLprim value caml_do_decrypt(value callback, value data) { - CAMLparam2(callback, data); +CAMLprim value caml_do_decrypt(value decrypt_callback, value data) { + CAMLparam2(decrypt_callback, data); CAMLlocal3(res, exn, cb_data); - char *dataString = String_val(data); + const char *dataString = String_val(data); int strLen = caml_string_length(data); SECItem *decoded = NSSBase64_DecodeBuffer(NULL, NULL, dataString, strLen); SECStatus rv; @@ -118,14 +118,14 @@ /* Base64 decoding succeeded */ /* Build the argument to password_func ((bool -> string) * exn option) */ cb_data = caml_alloc_tuple(2); - Store_field(cb_data, 0, callback); + Store_field(cb_data, 0, decrypt_callback); Store_field(cb_data, 1, Val_unit); /* None */ /* Decrypt */ rv = PK11SDR_Decrypt(decoded, &result, &cb_data); SECITEM_ZfreeItem(decoded, PR_TRUE); if (rv == SECSuccess) { res = caml_alloc_string(result.len); - memcpy(String_val(res), result.data, result.len); + memcpy(Bytes_val(res), result.data, result.len); SECITEM_ZfreeItem(&result, PR_FALSE); CAMLreturn(res); }