SSHAuthorize.Mod 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  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. PROCEDURE GetPW( CONST host, user: ARRAY OF CHAR; VAR pw: ARRAY OF CHAR );
  21. VAR n: Password;
  22. BEGIN
  23. n := passwords; pw := "";
  24. WHILE n # NIL DO
  25. IF (n.host = host) & (n.user = user) THEN COPY( n.pw, pw ); RETURN END;
  26. n := n.next
  27. END
  28. END GetPW;
  29. PROCEDURE AddPW( CONST host, user, pw: ARRAY OF CHAR );
  30. VAR n, p: Password;
  31. BEGIN
  32. n := passwords;
  33. WHILE n # NIL DO
  34. IF (n.host = host) & (n.user = user) THEN
  35. (* replace pw *)
  36. COPY( pw, n.pw ); RETURN
  37. END;
  38. p := n; n := n.next
  39. END;
  40. IF p = NIL THEN NEW( passwords ); p := passwords
  41. ELSE NEW( p.next ); p := p.next
  42. END;
  43. p.next := NIL;
  44. COPY( host, p.host );
  45. COPY( user, p.user );
  46. COPY( pw, p.pw )
  47. END AddPW;
  48. PROCEDURE RequestService( VAR ssh: Connection; CONST service: ARRAY OF CHAR ): BOOLEAN;
  49. VAR p: T.Packet; d: LONGINT; buf: ARRAY 256 OF CHAR;
  50. BEGIN
  51. NEW( p, ServiceRequest, 256 );
  52. p.AppString( service );
  53. ssh.SendPacket( p );
  54. IF ssh.ReceivePacket( buf, d ) = ServiceAccept THEN RETURN TRUE
  55. ELSE ssh.Disconnect( 11, "" ); RETURN FALSE
  56. END
  57. END RequestService;
  58. PROCEDURE RequestAuthorizeNone( ssh: Connection; CONST user: ARRAY OF CHAR ): BOOLEAN;
  59. VAR p: T.Packet; msg: ARRAY 512 OF CHAR; len: LONGINT;
  60. BEGIN
  61. NEW( p, UserauthRequest, 256 );
  62. p.AppString( user );
  63. p.AppString( "ssh-connection" );
  64. p.AppString( "none" );
  65. ssh.SendPacket( p );
  66. RETURN ssh.ReceivePacket( msg, len ) = UserauthSuccess
  67. END RequestAuthorizeNone;
  68. PROCEDURE RequestConnPW( ssh: Connection; CONST user, host: ARRAY OF CHAR; try: LONGINT );
  69. VAR p: T.Packet; headline, pw: ARRAY 64 OF CHAR;
  70. BEGIN
  71. headline := "SSH: Enter Password for ";
  72. Strings.Append( headline, user );
  73. Strings.Append( headline, "@" );
  74. Strings.Append( headline, host );
  75. NEW( p, UserauthRequest, 1024 );
  76. p.AppString( user );
  77. p.AppString( "ssh-connection" );
  78. p.AppString( "password" );
  79. p.AppChar( 0X );
  80. IF try = 1 THEN GetPW( host, user, pw ) ELSE pw := "" END;
  81. IF pw = "" THEN
  82. Beep.Beep( 1000 );
  83. IGNORE WMDialogs.QueryPassword( headline, pw);
  84. AddPW( host, user, pw );
  85. END;
  86. p.AppString( pw );
  87. ssh.SendPacket( p )
  88. END RequestConnPW;
  89. PROCEDURE AuthorizePasswd( ssh: Connection; CONST host, user: ARRAY OF CHAR ): BOOLEAN;
  90. VAR
  91. msg: ARRAY 2048 OF CHAR;
  92. len: LONGINT; try: INTEGER;
  93. BEGIN
  94. try := 1;
  95. RequestConnPW( ssh, user, host, try );
  96. LOOP
  97. CASE ssh.ReceivePacket( msg, len ) OF
  98. | UserauthBanner:
  99. U.PrintBufferString( msg, 1 ); Out.Ln;
  100. | UserauthSuccess:
  101. Out.String( "password authentication succeeded" ); Out.Ln;
  102. RETURN TRUE
  103. | UserauthFailure:
  104. IF try > 1 THEN
  105. Out.String( "password authentication failed" ); Out.Ln;
  106. RETURN FALSE
  107. ELSE
  108. INC( try );
  109. RequestConnPW( ssh, user, host, try )
  110. END
  111. ELSE
  112. Out.String( "SSHAuthorization.AuthorizePasswd: protocol error: got " ); Out.Int( ORD( msg[0] ), 3 ); Out.Ln;
  113. RETURN FALSE
  114. END
  115. END
  116. END AuthorizePasswd;
  117. PROCEDURE MakePubKeyBlob( VAR buf: ARRAY OF CHAR; VAR len: LONGINT );
  118. BEGIN
  119. len := 0;
  120. U.PutString( buf, len, "ssh-rsa" );
  121. U.PutBigNumber( buf, len, pubKey.exponent );
  122. U.PutBigNumber( buf, len, pubKey.modulus );
  123. END MakePubKeyBlob;
  124. PROCEDURE CheckAuthorizeKey( ssh: Connection; CONST user: ARRAY OF CHAR ): BOOLEAN;
  125. VAR p: T.Packet; buf: ARRAY 512 OF CHAR; len :LONGINT;
  126. BEGIN
  127. MakePubKeyBlob( buf, len );
  128. NEW( p, UserauthRequest, 1024 );
  129. p.AppString( user );
  130. p.AppString( "ssh-connection" );
  131. p.AppString( "publickey" );
  132. p.AppChar( 0X ); (* false *)
  133. p.AppString( "ssh-rsa" );
  134. p.AppArray( buf, 0, len );
  135. ssh.SendPacket( p );
  136. IF ssh.ReceivePacket( buf, len ) # UserauthPkOk THEN
  137. U.PrintBufferString( buf, 1 ); Out.Ln;
  138. RETURN FALSE
  139. END;
  140. RETURN TRUE
  141. END CheckAuthorizeKey;
  142. PROCEDURE RequestAuthorizeKey( ssh: Connection; CONST user: ARRAY OF CHAR ): BOOLEAN;
  143. CONST
  144. Asn1DerSha1 = "3021300906052B0E03021A05000414";
  145. HashLen = 20;
  146. MsgLen = 15 + HashLen;
  147. EmSize = 256;
  148. PadLen = EmSize - MsgLen;
  149. VAR
  150. p: T.Packet;
  151. blob, sig, buf: ARRAY 512 OF CHAR; pos, blen, len :LONGINT;
  152. signature: B.BigNumber;
  153. sha1: SHA1.Hash;
  154. em: ARRAY EmSize OF CHAR;
  155. i: LONGINT;
  156. BEGIN
  157. ASSERT( privKey.size = 2048 );
  158. MakePubKeyBlob( blob, blen );
  159. NEW( sha1 ); sha1.Initialize;
  160. pos := 0;
  161. U.PutArray( buf, pos, ssh.sessionId, 0, 20 );
  162. U.PutChar( buf, pos, UserauthRequest );
  163. U.PutString( buf, pos, user );
  164. U.PutString( buf, pos, "ssh-connection" );
  165. U.PutString( buf, pos, "publickey" );
  166. U.PutChar( buf, pos, 1X );
  167. U.PutString( buf, pos, "ssh-rsa" );
  168. U.PutArray( buf, pos, blob, 0, blen );
  169. sha1.Update( buf, 0, pos );
  170. (* padding PKCS1 type 1 *)
  171. em[0] := 0X; em[1] := 1X;
  172. FOR i := 2 TO PadLen - 2 DO em[i] := 0FFX END;
  173. em[PadLen - 1] := 0X;
  174. U.Hex2Bin( Asn1DerSha1, 0, em, EmSize - MsgLen, 15 );
  175. sha1.GetHash( em, EmSize - HashLen );
  176. signature := privKey.Sign( em, EmSize );
  177. pos := 0;
  178. U.PutString( sig, pos, "ssh-rsa" );
  179. U.PutBigNumber( sig, pos, signature );
  180. NEW( p, UserauthRequest, 1024 );
  181. p.AppString( user );
  182. p.AppString( "ssh-connection" );
  183. p.AppString( "publickey" );
  184. p.AppChar( 1X ); (* true *)
  185. p.AppString( "ssh-rsa" );
  186. p.AppArray( blob, 0, blen );
  187. p.AppArray( sig, 0, pos );
  188. ssh.SendPacket( p );
  189. IF ssh.ReceivePacket( buf, len ) # UserauthSuccess THEN
  190. U.PrintBufferString( buf, 1 ); Out.Ln;
  191. Out.String( "public key authentication failed" ); Out.Ln;
  192. RETURN FALSE
  193. END;
  194. Out.String( "public key authentication succeeded" ); Out.Ln;
  195. RETURN TRUE
  196. END RequestAuthorizeKey;
  197. PROCEDURE AuthorizeKey( ssh: Connection; CONST user: ARRAY OF CHAR ): BOOLEAN;
  198. CONST
  199. headline = "enter passphrase for opening your private key";
  200. VAR
  201. f: Files.File; r: Files.Reader; i: LONGINT;
  202. pw: ARRAY 64 OF CHAR;
  203. BEGIN
  204. IF privKey = NIL THEN
  205. f := Files.Old( G.PrivateKeyFile );
  206. IF f = NIL THEN
  207. Out.String( "private key '" ); Out.String( G.PrivateKeyFile );
  208. Out.String( "' not found" ); Out.Ln;
  209. RETURN FALSE
  210. END;
  211. Files.OpenReader( r, f, 0 ); i := 0;
  212. REPEAT
  213. Beep.Beep( 1000 );
  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 = 2);
  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. END SSHAuthorize.