package opasswd

  1. Overview
  2. Docs

Source file passwd.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
open Ctypes
open Foreign

type file_descr = unit ptr
let file_descr : file_descr typ = ptr void

let fopen =
  foreign ~check_errno:true "fopen" (string @-> string @-> returning file_descr)
let fclose' = foreign ~check_errno:true "fclose" (file_descr @-> returning int)
let fclose fd = fclose' fd |> ignore

type t = {
  name   : string;
  passwd : string;
  (* According to bits/typesizes.h uid_t and gid_t are uint32 *)
  uid    : int; (* uid    : uid_t; *)
  gid    : int; (* gid    : gid_t; *)
  gecos  : string;
  dir    : string;
  shell  : string;
}

type db = t list

type passwd_t

let passwd_t : passwd_t structure typ = structure "passwd"

let pw_name   = field passwd_t "pw_name" (ptr char)
let pw_passwd = field passwd_t "pw_passwd" (ptr char)
let pw_uid    = field passwd_t "pw_uid" uint32_t
let pw_gid    = field passwd_t "pw_gid" uint32_t
let pw_gecos  = field passwd_t "pw_gecos" (ptr char)
let pw_dir    = field passwd_t "pw_dir" (ptr char)
let pw_shell  = field passwd_t "pw_shell" (ptr char)

let () = seal passwd_t

let ptr_char_to_string p = coerce (ptr char) string p

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_passwd_t pw = {
  name   = getf pw pw_name |> ptr_char_to_string;
  passwd = getf pw pw_passwd |> ptr_char_to_string;
  uid    = getf pw pw_uid |> Unsigned.UInt32.to_int;
  gid    = getf pw pw_gid |> Unsigned.UInt32.to_int;
  gecos  = getf pw pw_gecos |> ptr_char_to_string;
  dir    = getf pw pw_dir |> ptr_char_to_string;
  shell  = getf pw pw_shell |> ptr_char_to_string;
}

let from_passwd_t_opt = function
  | None -> None
  | Some pw -> Some (from_passwd_t !@pw)

module Mem : sig
  type mem
  val to_mem : t -> mem
  val passwd_addr_of_mem : mem -> (passwd_t, [`Struct]) Ctypes.structured Ctypes.ptr
end = struct
  type mem = passwd_t structure * char carray * char carray * char carray * char carray * char carray

  let to_mem pw =
    let name = string_to_char_array pw.name in
    let passwd = string_to_char_array pw.passwd in
    let gecos = string_to_char_array pw.gecos in
    let dir = string_to_char_array pw.dir in
    let shell = string_to_char_array pw.shell in
    let pw_t : passwd_t structure = make passwd_t in
    setf pw_t pw_name (CArray.start name);
    setf pw_t pw_passwd (CArray.start passwd);
    setf pw_t pw_uid (Unsigned.UInt32.of_int pw.uid);
    setf pw_t pw_gid (Unsigned.UInt32.of_int pw.gid);
    setf pw_t pw_gecos (CArray.start gecos);
    setf pw_t pw_dir (CArray.start dir);
    setf pw_t pw_shell (CArray.start shell);
    (pw_t, name, passwd, gecos, dir, shell)

  let passwd_addr_of_mem (sp_t, _, _, _, _, _) = addr sp_t
end

let passwd_file = "/etc/passwd"

let getpwnam' =
  foreign ~check_errno:true "getpwnam" (string @-> returning (ptr_opt passwd_t))
let getpwnam name = getpwnam' name |> from_passwd_t_opt

let getpwuid' =
  foreign ~check_errno:true "getpwuid" (int @-> returning (ptr_opt passwd_t))
let getpwuid uid = getpwuid' uid |> from_passwd_t_opt

let getpwent' =
  foreign ~check_errno:true "getpwent" (void @-> returning (ptr_opt passwd_t))
let getpwent () = getpwent' () |> from_passwd_t_opt

let setpwent = foreign ~check_errno:true "setpwent" (void @-> returning void)
let endpwent = foreign ~check_errno:true "endpwent" (void @-> returning void)

let putpwent' =
  foreign ~check_errno:true "putpwent" (ptr passwd_t @-> file_descr @-> returning int)
let putpwent fd pw =
  let mem = Mem.to_mem pw in
  putpwent' (Mem.passwd_addr_of_mem mem) fd |> ignore

let get_db () =
  let rec loop acc =
    match getpwent () with
    | None -> endpwent () ; acc
    | Some pw -> loop (pw :: acc)
  in
  setpwent () ;
  loop [] |> List.rev

let update_db db ent =
  let rec loop acc = function
    | [] -> List.rev acc
    | e :: es when e.name = ent.name -> loop (ent::acc) es
    | e :: es -> loop (e::acc) es
  in loop [] db

let write_db ?(file=passwd_file) db =
  let fd = fopen file "r+" in
  List.iter (putpwent fd) db;
  fclose fd

let to_string p =
  let str i =
    if i >= 0
    then string_of_int i
    else "" in
  Printf.sprintf "%s:%s:%s:%s:%s:%s:%s"
    p.name
    p.passwd
    (str p.uid)
    (str p.gid)
    p.gecos
    p.dir
    p.shell

let db_to_string db =
  db
  |> List.map to_string
  |> String.concat "\n"

(* Local Variables: *)
(* indent-tabs-mode: nil *)
(* End: *)
OCaml

Innovation. Community. Security.