SSHAuthorize.Mod 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. MODULE SSHAuthorize; (* g.f. 2001.12.12 *)
  2. IMPORT T := SSHTransport, U := CryptoUtils, B := CryptoBigNumbers, RSA := CryptoRSA, G := SSHGlobals,
  3. SHA1 := CryptoSHA1, Strings, Out := KernelLog, Files, WMDialogs, Beep;
  4. TYPE
  5. Connection* = T.Connection;
  6. CONST
  7. Closed* = T.Closed; Connected* = T.Connected;
  8. ServiceRequest = 5X; ServiceAccept = 6X;
  9. UserauthRequest = 32X; UserauthFailure = 33X;
  10. UserauthSuccess = 34X; UserauthBanner = 35X;
  11. UserauthPkOk = 3CX;
  12. TYPE
  13. Password = POINTER TO RECORD
  14. next: Password;
  15. host, user, pw: ARRAY 64 OF CHAR;
  16. END;
  17. VAR
  18. passwords: Password;
  19. privKey, pubKey: RSA.Key;
  20. hexd: ARRAY 17 OF CHAR;
  21. PROCEDURE GetPW( CONST host, user: ARRAY OF CHAR; VAR pw: ARRAY OF CHAR );
  22. VAR n: Password;
  23. BEGIN
  24. n := passwords; pw := "";
  25. WHILE n # NIL DO
  26. IF (n.host = host) & (n.user = user) THEN COPY( n.pw, pw ); RETURN END;
  27. n := n.next
  28. END
  29. END GetPW;
  30. PROCEDURE AddPW( CONST host, user, pw: ARRAY OF CHAR );
  31. VAR n, p: Password;
  32. BEGIN
  33. n := passwords;
  34. WHILE n # NIL DO
  35. IF (n.host = host) & (n.user = user) THEN
  36. (* replace pw *)
  37. COPY( pw, n.pw ); RETURN
  38. END;
  39. p := n; n := n.next
  40. END;
  41. IF p = NIL THEN NEW( passwords ); p := passwords
  42. ELSE NEW( p.next ); p := p.next
  43. END;
  44. p.next := NIL;
  45. COPY( host, p.host );
  46. COPY( user, p.user );
  47. COPY( pw, p.pw )
  48. END AddPW;
  49. PROCEDURE RequestService( VAR ssh: Connection; CONST service: ARRAY OF CHAR ): BOOLEAN;
  50. VAR p: T.Packet; d: LONGINT; buf: ARRAY 256 OF CHAR;
  51. BEGIN
  52. NEW( p, ServiceRequest, 256 );
  53. p.AppString( service );
  54. ssh.SendPacket( p );
  55. IF ssh.ReceivePacket( buf, d ) = ServiceAccept THEN RETURN TRUE
  56. ELSE ssh.Disconnect( 11, "" ); RETURN FALSE
  57. END
  58. END RequestService;
  59. PROCEDURE RequestAuthorizeNone( ssh: Connection; CONST user: ARRAY OF CHAR ): BOOLEAN;
  60. VAR p: T.Packet; msg: ARRAY 512 OF CHAR; len: LONGINT;
  61. BEGIN
  62. NEW( p, UserauthRequest, 256 );
  63. p.AppString( user );
  64. p.AppString( "ssh-connection" );
  65. p.AppString( "none" );
  66. ssh.SendPacket( p );
  67. RETURN ssh.ReceivePacket( msg, len ) = UserauthSuccess
  68. END RequestAuthorizeNone;
  69. PROCEDURE RequestConnPW( ssh: Connection; CONST user, host: ARRAY OF CHAR; try: LONGINT );
  70. VAR p: T.Packet; headline, pw: ARRAY 64 OF CHAR; ignore: LONGINT;
  71. BEGIN
  72. headline := "SSH: Enter Password for ";
  73. Strings.Append( headline, user );
  74. Strings.Append( headline, "@" );
  75. Strings.Append( headline, host );
  76. NEW( p, UserauthRequest, 1024 );
  77. p.AppString( user );
  78. p.AppString( "ssh-connection" );
  79. p.AppString( "password" );
  80. p.AppChar( 0X );
  81. IF try = 1 THEN GetPW( host, user, pw ) ELSE pw := "" END;
  82. IF pw = "" THEN
  83. Beep.Beep( 1000 );
  84. ignore := WMDialogs.QueryPassword( headline, pw);
  85. AddPW( host, user, pw );
  86. END;
  87. p.AppString( pw );
  88. ssh.SendPacket( p )
  89. END RequestConnPW;
  90. PROCEDURE AuthorizePasswd( ssh: Connection; CONST host, user: ARRAY OF CHAR ): BOOLEAN;
  91. VAR
  92. msg: ARRAY 2048 OF CHAR;
  93. len: LONGINT; try: INTEGER;
  94. BEGIN
  95. try := 1;
  96. RequestConnPW( ssh, user, host, try );
  97. LOOP
  98. CASE ssh.ReceivePacket( msg, len ) OF
  99. | UserauthBanner:
  100. U.PrintBufferString( msg, 1 ); Out.Ln;
  101. | UserauthSuccess:
  102. Out.String( "password authentication succeeded" ); Out.Ln;
  103. RETURN TRUE
  104. | UserauthFailure:
  105. IF try > 2 THEN
  106. Out.String( "password authentication failed" ); Out.Ln;
  107. RETURN FALSE
  108. ELSE
  109. INC( try );
  110. RequestConnPW( ssh, user, host, try )
  111. END
  112. ELSE
  113. Out.String( "SSHAuthorization.AuthorizePasswd: protocol error: got " ); Out.Int( ORD( msg[0] ), 3 ); Out.Ln;
  114. RETURN FALSE
  115. END
  116. END
  117. END AuthorizePasswd;
  118. PROCEDURE MakePubKeyBlob( VAR buf: ARRAY OF CHAR; VAR len: LONGINT );
  119. BEGIN
  120. len := 0;
  121. U.PutString( buf, len, "ssh-rsa" );
  122. U.PutBigNumber( buf, len, pubKey.exponent );
  123. U.PutBigNumber( buf, len, pubKey.modulus );
  124. END MakePubKeyBlob;
  125. PROCEDURE CheckAuthorizeKey( ssh: Connection; CONST user: ARRAY OF CHAR ): BOOLEAN;
  126. VAR p: T.Packet; buf: ARRAY 512 OF CHAR; len :LONGINT;
  127. BEGIN
  128. MakePubKeyBlob( buf, len );
  129. NEW( p, UserauthRequest, 1024 );
  130. p.AppString( user );
  131. p.AppString( "ssh-connection" );
  132. p.AppString( "publickey" );
  133. p.AppChar( 0X ); (* false *)
  134. p.AppString( "ssh-rsa" );
  135. p.AppArray( buf, 0, len );
  136. ssh.SendPacket( p );
  137. IF ssh.ReceivePacket( buf, len ) # UserauthPkOk THEN
  138. U.PrintBufferString( buf, 1 ); Out.Ln;
  139. RETURN FALSE
  140. END;
  141. RETURN TRUE
  142. END CheckAuthorizeKey;
  143. PROCEDURE RequestAuthorizeKey( ssh: Connection; CONST user: ARRAY OF CHAR ): BOOLEAN;
  144. CONST
  145. Asn1DerSha1 = "3021300906052B0E03021A05000414";
  146. HashLen = 20;
  147. MsgLen = 15 + HashLen;
  148. EmSize = 256;
  149. PadLen = EmSize - MsgLen;
  150. VAR
  151. p: T.Packet;
  152. blob, sig, buf: ARRAY 512 OF CHAR; pos, blen, len :LONGINT;
  153. signature: B.BigNumber;
  154. sha1: SHA1.Hash;
  155. em: ARRAY EmSize OF CHAR;
  156. i: LONGINT;
  157. BEGIN
  158. ASSERT( privKey.size = 2048 );
  159. MakePubKeyBlob( blob, blen );
  160. NEW( sha1 ); sha1.Initialize;
  161. pos := 0;
  162. U.PutArray( buf, pos, ssh.sessionId, 0, 20 );
  163. U.PutChar( buf, pos, UserauthRequest );
  164. U.PutString( buf, pos, user );
  165. U.PutString( buf, pos, "ssh-connection" );
  166. U.PutString( buf, pos, "publickey" );
  167. U.PutChar( buf, pos, 1X );
  168. U.PutString( buf, pos, "ssh-rsa" );
  169. U.PutArray( buf, pos, blob, 0, blen );
  170. sha1.Update( buf, 0, pos );
  171. (* padding PKCS1 type 1 *)
  172. em[0] := 0X; em[1] := 1X;
  173. FOR i := 2 TO PadLen - 2 DO em[i] := 0FFX END;
  174. em[PadLen - 1] := 0X;
  175. U.Hex2Bin( Asn1DerSha1, 0, em, EmSize - MsgLen, 15 );
  176. sha1.GetHash( em, EmSize - HashLen );
  177. signature := privKey.Sign( em, EmSize );
  178. pos := 0;
  179. U.PutString( sig, pos, "ssh-rsa" );
  180. U.PutBigNumber( sig, pos, signature );
  181. NEW( p, UserauthRequest, 1024 );
  182. p.AppString( user );
  183. p.AppString( "ssh-connection" );
  184. p.AppString( "publickey" );
  185. p.AppChar( 1X ); (* true *)
  186. p.AppString( "ssh-rsa" );
  187. p.AppArray( blob, 0, blen );
  188. p.AppArray( sig, 0, pos );
  189. ssh.SendPacket( p );
  190. IF ssh.ReceivePacket( buf, len ) # UserauthSuccess THEN
  191. U.PrintBufferString( buf, 1 ); Out.Ln;
  192. Out.String( "public key authentication failed" ); Out.Ln;
  193. RETURN FALSE
  194. END;
  195. Out.String( "public key authentication succeeded" ); Out.Ln;
  196. RETURN TRUE
  197. END RequestAuthorizeKey;
  198. PROCEDURE AuthorizeKey( ssh: Connection; CONST user: ARRAY OF CHAR ): BOOLEAN;
  199. CONST
  200. headline = "enter passphrase for opening your private key";
  201. VAR
  202. f: Files.File; r: Files.Reader; i, ignore: LONGINT;
  203. pw, str: ARRAY 64 OF CHAR;
  204. BEGIN
  205. IF privKey = NIL THEN
  206. f := Files.Old( G.PrivateKeyFile );
  207. IF f = NIL THEN
  208. Out.String( "private key '" ); Out.String( G.PrivateKeyFile );
  209. Out.String( "' not found" ); Out.Ln;
  210. RETURN FALSE
  211. END;
  212. Files.OpenReader( r, f, 0 ); i := 0;
  213. REPEAT
  214. ignore := WMDialogs.QueryPassword( headline, pw );
  215. r.SetPos( 0 );
  216. privKey := RSA.LoadPrivateKey( r, pw );
  217. INC( i )
  218. UNTIL (privKey # NIL) OR (i = 3);
  219. IF privKey = NIL THEN
  220. Out.String( "### error: wrong passphrase" ); Out.Ln; RETURN FALSE
  221. END;
  222. END;
  223. f := Files.Old( G.PublicKeyFile );
  224. IF f = NIL THEN
  225. Out.String( "### error: public key not found" ); Out.Ln;
  226. RETURN FALSE
  227. END;
  228. Files.OpenReader( r, f, 0 );
  229. pubKey := RSA.LoadPublicKey( r );
  230. IF CheckAuthorizeKey( ssh, user ) THEN
  231. RETURN RequestAuthorizeKey( ssh, user )
  232. END;
  233. RETURN FALSE
  234. END AuthorizeKey;
  235. (** Open an outhorized SSH connection, returns NIL on failure *)
  236. PROCEDURE OpenConnection*( CONST host, user: ARRAY OF CHAR ): Connection;
  237. VAR
  238. ssh: Connection; authorized: BOOLEAN;
  239. BEGIN
  240. NEW( ssh, host );
  241. IF ssh.state = Connected THEN
  242. IF RequestService( ssh, "ssh-userauth" ) THEN
  243. authorized := RequestAuthorizeNone( ssh, user );
  244. IF ~authorized THEN
  245. Out.String( "trying public key authentication" ); Out.Ln;
  246. authorized := AuthorizeKey( ssh, user );
  247. END;
  248. IF ~authorized THEN
  249. Out.String( "trying password authentication" ); Out.Ln;
  250. authorized := AuthorizePasswd( ssh, host, user )
  251. END;
  252. IF ~authorized THEN
  253. ssh.Disconnect( 11, "" ); RETURN NIL
  254. END;
  255. RETURN ssh
  256. ELSE
  257. RETURN NIL
  258. END
  259. ELSE
  260. RETURN NIL
  261. END
  262. END OpenConnection;
  263. BEGIN
  264. hexd := "0123456789ABCDEF"
  265. END SSHAuthorize.