package opasswd
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file shadow.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
open Ctypes open Foreign type t = { name : string; passwd : string; last_chg : int64; min : int64; max : int64; warn : int64; inact : int64; expire : int64; flag : int; } type db = t list type shadow_t let shadow_t : shadow_t structure typ = structure "passwd" let sp_name = field shadow_t "sp_name" (ptr char) let sp_passwd = field shadow_t "sp_passwd" (ptr char) let sp_last_chg = field shadow_t "sp_lastchg" long let sp_min = field shadow_t "sp_min" long let sp_max = field shadow_t "sp_max" long let sp_warn = field shadow_t "sp_warn" long let sp_inact = field shadow_t "sp_inact" long let sp_expire = field shadow_t "sp_expire" long let sp_flag = field shadow_t "sp_flag" ulong let () = seal shadow_t let ptr_char_to_string = coerce (ptr char) string let string_to_char_array s = let len = String.length s in let buf = CArray.make char ~initial:'\x00' (len+1) in String.iteri (fun idx c -> CArray.set buf idx c) s; buf let from_shadow_t sp = { name = getf sp sp_name |> ptr_char_to_string; passwd = getf sp sp_passwd |> ptr_char_to_string; last_chg = getf sp sp_last_chg |> Signed.Long.to_int64; min = getf sp sp_min |> Signed.Long.to_int64; max = getf sp sp_max |> Signed.Long.to_int64; warn = getf sp sp_warn |> Signed.Long.to_int64; inact = getf sp sp_inact |> Signed.Long.to_int64; expire = getf sp sp_expire |> Signed.Long.to_int64; flag = getf sp sp_flag |> Unsigned.ULong.to_int; } let from_shadow_t_opt = function | None -> None | Some sp -> Some (from_shadow_t !@sp) (* Wrap up all the allocating functions into this module that returns an opaque type. This tries to make sure that the lifetime of all the allocated memory is the same. The one dangerous thing is 'shadow_addr_of_mem' - but it's a bit more obvious that you've got to keep the 'mem' value alive than the individual fields within it too. Note that we actually hide `shadow_addr_of_mem` from outside this module via the mli file. Paranoia! *) module Mem : sig type mem val to_mem : t -> mem val from_mem : mem -> t val shadow_addr_of_mem : mem -> (shadow_t, [ `Struct ]) Ctypes.structured Ctypes.ptr end = struct type mem = shadow_t structure * char carray * char carray let to_mem sp = let name = string_to_char_array sp.name in let passwd = string_to_char_array sp.passwd in let sp_t : shadow_t structure = make shadow_t in setf sp_t sp_name (CArray.start name); setf sp_t sp_passwd (CArray.start passwd); setf sp_t sp_last_chg (Signed.Long.of_int64 sp.last_chg); setf sp_t sp_min (Signed.Long.of_int64 sp.min); setf sp_t sp_max (Signed.Long.of_int64 sp.max); setf sp_t sp_warn (Signed.Long.of_int64 sp.warn); setf sp_t sp_inact (Signed.Long.of_int64 sp.inact); setf sp_t sp_expire (Signed.Long.of_int64 sp.expire); setf sp_t sp_flag (Unsigned.ULong.of_int sp.flag); (sp_t,name,passwd) let from_mem (sp_t, _name, _passwd) = from_shadow_t sp_t let shadow_addr_of_mem (sp_t, _, _) = addr sp_t end let shadow_file = "/etc/shadow" let getspnam' = foreign ~check_errno:true "getspnam" (string @-> returning (ptr_opt shadow_t)) let getspnam name = getspnam' name |> from_shadow_t_opt let getspent' = foreign ~check_errno:true "getspent" (void @-> returning (ptr_opt shadow_t)) let getspent () = getspent' () |> from_shadow_t_opt let setspent = foreign ~check_errno:true "setspent" (void @-> returning void) let endspent = foreign ~check_errno:true "endspent" (void @-> returning void) let putspent' = foreign ~check_errno:true "putspent" (ptr shadow_t @-> Passwd.file_descr @-> returning int) let putspent fd sp = let mem = Mem.to_mem sp in putspent' (Mem.shadow_addr_of_mem mem) fd |> ignore let lckpwdf' = foreign "lckpwdf" (void @-> returning int) let lckpwdf () = lckpwdf' () = 0 let ulckpwdf' = foreign "ulckpwdf" (void @-> returning int) let ulckpwdf () = ulckpwdf' () = 0 let shadow_enabled () = try Unix.access shadow_file [Unix.F_OK]; true with _ -> false let get_db () = let rec loop acc = match getspent () with | None -> endspent () ; acc | Some sp -> loop (sp :: acc) in setspent () ; loop [] |> List.rev let update_db db pw = let rec loop acc = function | [] -> List.rev acc | e :: es when e.name = pw.name -> loop (pw::acc) es | e :: es -> loop (e::acc) es in loop [] db let write_db ?(file=shadow_file) db = let fd = Passwd.fopen file "r+" in List.iter (putspent fd) db; Passwd.fclose fd let to_string p = let str i = if (Int64.compare i 0L) >= 0 then Int64.to_string i else "" in Printf.sprintf "%s:%s:%s:%s:%s:%s:%s:%s:%d" p.name p.passwd (str p.last_chg) (str p.min) (str p.max) (str p.warn) (str p.inact) (str p.expire) p.flag let db_to_string db = db |> List.map to_string |> String.concat "\n" let with_lock f = if lckpwdf () then begin let ret = f () in ignore (ulckpwdf ()); ret end else raise Unix.(Unix_error (EAGAIN, "with_lock", "Couldn't acquire shadow lock")) (* Local Variables: *) (* indent-tabs-mode: nil *) (* End: *)