Plan 9 from Bell Labs’s /usr/web/sources/contrib/axel/tcl/9pvfs/p9sk1.tcl

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


# small 9p implementation in tcl by Axel.Belinfante@cs.utwente.nl

# it needs tclDES from http://tcldes.sourceforge.net/
# I have used version 0.8 with good success

# this is based on the python 9P implementation by Tim Newsham.
# http://lava.net/~newsham/plan9/

package require tclDES

namespace eval p9sk1 {
	variable TickReqLen 141
	variable TickLen 72
	variable AuthLen 13

	variable AuthTreq 1
	variable AuthChal 2
	variable AuthPass 3
	variable AuthOK 4
	variable AuthErr 5 
	variable AuthMod 6 

	variable AuthTs 64
	variable AuthTc 65 
	variable AuthAs 66
	variable AuthAc 67
	variable AuthTp 68
	variable AuthHr 69

	variable AUTHPORT 567

	variable _par [list \
	0x01 0x02 0x04 0x07 0x08 0x0b 0x0d 0x0e \
	0x10 0x13 0x15 0x16 0x19 0x1a 0x1c 0x1f \
	0x20 0x23 0x25 0x26 0x29 0x2a 0x2c 0x2f \
	0x31 0x32 0x34 0x37 0x38 0x3b 0x3d 0x3e \
	0x40 0x43 0x45 0x46 0x49 0x4a 0x4c 0x4f \
	0x51 0x52 0x54 0x57 0x58 0x5b 0x5d 0x5e \
	0x61 0x62 0x64 0x67 0x68 0x6b 0x6d 0x6e \
	0x70 0x73 0x75 0x76 0x79 0x7a 0x7c 0x7f \
	0x80 0x83 0x85 0x86 0x89 0x8a 0x8c 0x8f \
	0x91 0x92 0x94 0x97 0x98 0x9b 0x9d 0x9e \
	0xa1 0xa2 0xa4 0xa7 0xa8 0xab 0xad 0xae \
	0xb0 0xb3 0xb5 0xb6 0xb9 0xba 0xbc 0xbf \
	0xc1 0xc2 0xc4 0xc7 0xc8 0xcb 0xcd 0xce \
	0xd0 0xd3 0xd5 0xd6 0xd9 0xda 0xdc 0xdf \
	0xe0 0xe3 0xe5 0xe6 0xe9 0xea 0xec 0xef \
	0xf1 0xf2 0xf4 0xf7 0xf8 0xfb 0xfd 0xfe \
	]

}
proc p9sk1::pad {str l {padch {}}} {
	set n [expr $l - [string length $str]]
	set s $str
	if {$padch == {}} {
		set padch [binary format x]
	}
	append s [string repeat $padch $n]
	return $s
} 



# Expand a 7-byte DES key into an 8-byte DES key
proc p9sk1::expandKey {key} {
	variable _par

	binary scan $key c* tbuf
	set k {}
	foreach x $tbuf {
		lappend k [expr $x & 0xff]
	}
	lappend k64 [expr  [lindex $k 0] >> 1]
	lappend k64 [expr ([lindex $k 1] >> 2) | ([lindex $k 0] << 6)]
	lappend k64 [expr ([lindex $k 2] >> 3) | ([lindex $k 1] << 5)]
	lappend k64 [expr ([lindex $k 3] >> 4) | ([lindex $k 2] << 4)]
	lappend k64 [expr ([lindex $k 4] >> 5) | ([lindex $k 3] << 3)]
	lappend k64 [expr ([lindex $k 5] >> 6) | ([lindex $k 4] << 2)]
	lappend k64 [expr ([lindex $k 6] >> 7) | ([lindex $k 5] << 1)]
	lappend k64 [expr  [lindex $k 6] << 0]
	foreach x $k64 {
		lappend r [lindex $_par [expr $x & 0x7f]]
	}
	return [binary format c* $r]
}
proc p9sk1::newKey {key} {
	set e [expandKey $key]
	return [::des::keyset create $e]
}
proc p9sk1::encrypt {key msg} {
	set r [::des::encrypt $key $msg]
	return $r
}
proc p9sk1::decrypt {key msg} {
	set r [::des::decrypt $key $msg]
	return $r
}
proc p9sk1::makeKey {password} {
	set password [string range $password 0 26]
	append password [binary format x]
	set n [expr [string length $password] - 1]
	set password [p9sk1::pad $password 28 { }]
	set buf $password
	while {1} {
		set ts [string range $buf 0 7]
		binary scan $ts c* tl
		set t {}
		foreach x $tl {
			lappend t [expr $x & 0xff]
		}
		set i 0
		set k {}
		while {$i < 7} {
			lappend k [expr ([lindex $t $i] >> $i) + ([lindex $t [expr $i + 1]] << (8-($i + 1))) & 0xff]
			incr i
		}
		set key [binary format c* $k]
		if {$n <= 8} {
			return $key
		}
		incr n -8
		if {$n < 8} {
			set buf [string range $buf $n end]
		} else {
			set buf [string range $buf 8 end]
		}
		set buf [string replace $buf 0 7 [::p9sk1::encrypt [newKey $key] [string range $buf 0 7]]]
	}
}

# XXX This is *NOT* a secure way to generate random strings!
# This should be fixed if this code is ever used in a serious manner.
proc p9sk1::randChars {n} {
	set i 0
	while {$i < $n} {
		lappend r [expr int(rand()*255)]
		incr i
	}
	return [binary format c* $r]
}

namespace eval 9p::marshal {
#	upvar #0 C_[set self](ks) ks
#	upvar #0 C_[set self](kn) kn

#	set ks None
#	set kn None
}

proc 9p::marshal::setKs {self k} {
	upvar #0 C_[set self](ks) ks

	set ks [::p9sk1::newKey $k]
}
proc 9p::marshal::setKn {self k} {
	upvar #0 C_[set self](kn) kn

	set kn [::p9sk1::newKey $k]
}
proc 9p::marshal::encrypt {self n key} {
	set idx [expr [lenBuf $self] - $n]
	incr n -1
	set dummy 0
	while {$dummy < [expr $n / 7]} {
		set end  [expr $idx + 8 -1]
		replaceBuf $self $idx $end [::p9sk1::encrypt $key [rangeBuf $self $idx $end]]
		incr idx 7
		incr dummy
	}
	if {$n % 7} {
		set end  [expr [lenBuf $self] - 1]
		set start  [expr $end - 8 + 1]
		replaceBuf $self $start $end [::p9sk1::encrypt $key [rangeBuf $self $start $end]]
	}
}
proc 9p::marshal::decrypt {self n key} {
	upvar #0 C_[set self](kn) kn

	if {[string compare $key None] == 0} {
		return
	}

	set m [expr $n -1 ]
	if {$m % 7} {
		set start [expr $n - 8]
		set end  [expr $n - 1]
		replaceBuf $self $start $end [::p9sk1::decrypt $key [rangeBuf $self $start $end]]
	}
	set idx [expr $m - ($m % 7)]
	set dummy 0
	while {$dummy < [expr $m / 7]} {
		incr idx -7
		set end  [expr $idx + 8 - 1]
		replaceBuf $self $idx $end [::p9sk1::decrypt $key [rangeBuf $self $idx $end]]
		incr dummy
	}
}
proc 9p::marshal::encPad {self x l} {
	encX $self [::p9sk1::pad  $x $l]
}
proc 9p::marshal::decPad {self l} {
	set x [decX $self $l]
	set z [binary format x]
	set idx [string first $z $x]
	if {$idx >= 0} {
		set x [string range $x 0 [expr $idx - 1]]
	}
	return $x
}
proc 9p::marshal::encChal {self x} {
	checkLen $x 8
	encX $self $x
}
proc 9p::marshal::decChal {self} {
	set r [decX $self 8]
	return $r
}
proc 9p::marshal::encTicketReq {self x} {
	enc1 $self [lindex $x 0]		;# type
	encPad $self [lindex $x 1] 28	;# authid
	encPad $self [lindex $x 2] 48	;# authdom
	encChal $self [lindex $x 3]		;# chal
	encPad $self [lindex $x 4] 28	;# hostid
	encPad $self [lindex $x 5] 28	;# uid
}
proc 9p::marshal::decTicketReq {self} {
	set r [list \
		[dec1 $self]	\
		[decPad $self 28] \
		[decPad $self 48] \
		[decChal $self] \
		[decPad $self 28] \
		[decPad $self 28] \
	]
	return $r
}
proc 9p::marshal::encTicket {self x} {
	upvar #0 C_[set self](ks) ks

	set num [lindex $x 0]
	set chal [lindex $x 1]
	set cuid [lindex $x 2]
	set suid [lindex $x 3]
	set key [lindex $x 4]

	checkLen $key 7

	enc1 $self $num
	encChal $self $chal
	encPad $self $cuid 28
	encPad $self $suid 28
	encX $sel $key

	encrypt $self [expr 1 + 8 + 28 + 28 + 7] $ks
}
proc 9p::marshal::decTicket {self} {
	upvar #0 C_[set self](ks) ks

	decrypt $self [expr 1 + 8 + 28 + 28 + 7] $ks
	set r [list \
		[dec1 $self]	\
		[decChal $self] \
		[decPad $self 28] \
		[decPad $self 28] \
		[decX $self 7] \
	]
	return $r
}
proc 9p::marshal::encAuth {self x} {
	upvar #0 C_[set self](kn) kn

	set num [lindex $x 0]
	set chal [lindex $x 1]
	set id [lindex $x 2]
	enc1 $self $num
	encChal $self $chal
	enc4 $self $id
	encrypt $self [expr 1 + 8 + 4] $kn
}
proc 9p::marshal::decAuth {self} {
	upvar #0 C_[set self](kn) kn

	decrypt $self [expr 1 + 8 + 4] $kn
	set r [list \
		[dec1 $self]	\
		[decChal $self] \
		[dec4 $self] \
	]
	return $r
}
proc 9p::marshal::encTattach {self x} {
	set tick [lindex $x 0]
	set auth [lindex $x 1]
	checkLen $tick 72
	encX $self $tick
	encAuth $self $auth
}
proc 9p::marshal::decTattach {self} {
	set r [list \
		[decX $self 72] \
		[decAuth $self] \
	]
	return $r
}

#	Connect to the auth server and request a set of tickets.
#	Con is an open handle to the auth server, sk1 is a handle
#	to a P9sk1 marshaller with Kc set and treq is a ticket request.
#	Return the (opaque) server ticket and the (decoded) client ticket.
proc p9sk1::getTicket {con sk1 treq} {
	variable AuthOK
	variable AuthErr

	::9p::marshal::setBuf $sk1 ""
	::9p::marshal::encTicketReq $sk1 $treq
	set x [::9p::marshal::getBuf $sk1]
	::9p::marshal::swrite $con $x
	set ch [::9p::marshal::sread $con 1]
	if {$ch == [binary format c $AuthErr]} {
		set err [::9p::marshal::sread $con 64]
		error "AuthsrvError $err"
	} elseif  {$ch != [binary format c $AuthOK]} {
		error "AuthsrvError invalid reply type [::9p::_dump $ch]"
	}
	set ctick [::9p::marshal::sread $con 72]
	set stick [::9p::marshal::sread $con 72]
	if {[expr [string length $ctick] + [string length $stick]] != [expr 2*72]} {
		error "AuthsrvError short auth reply"
	}
	::9p::marshal::setBuf $sk1 $ctick
	set ctl [::9p::marshal::decTicket $sk1]
	set r [list $ctl $stick]
	return $r
}

#	Authenticate ourselves to the server.
#	Cl is a P9 RpcClient, afid is the fid to use, user is the
#	user name, Kc is the user's key, authsrv and authport specify
#	the auth server to use for requesting tickets.
#
#	XXX perhaps better if the auth server can be prompted for
#	based on the domain in the negotiation.
proc p9sk1::clientAuth {cl afid user Kc authsrv {authport 567}} {
	variable TickReqLen
	variable AuthLen
	variable AuthTreq
	variable AuthTc
	variable AuthAc
	variable AuthAs

	set CHc [randChars 8]
	set sk1 aapje
	::9p::marshal::setKs $sk1 $Kc
	::9p::afidopen $cl $afid
	set gen 0

	# negotiate
	set proto [::9p::read $cl $afid 128]
	set v2 0
	if {[string compare [string range $proto 0 9] {v.2 p9sk1@}] == 0} {
		set v2 1
		set proto [string range $proto 4 end]
	}
	if {[string compare [string range $proto 0 5] {p9sk1@}] != 0} {
		error "AuthError unknown protocol $proto"
	}
	set idx [string first @ $proto]
	::9p::write $cl $afid [string replace $proto $idx $idx { }]
	if {$v2} {
		set ok [::9p::read $cl $afid 3]
		if {[string compare $ok OK[binary format x]] != 0} {
			error "AuthError v.2 protocol botch"
		}
	}
	
	# Tsession
	::9p::marshal::setBuf $sk1 ""
	::9p::marshal::encChal $sk1 $CHc
	::9p::write $cl $afid [::9p::marshal::getBuf $sk1]

	# Rsession
	::9p::marshal::setBuf $sk1 [::9p::read $cl $afid $TickReqLen]
	set treq [::9p::marshal::decTicketReq $sk1]
	if {$v2 && [lindex $treq 0] == 0} {
		# kenfs is fast and loose with auth formats
		set treq [lreplace $treq 0 0 $AuthTreq]
	}
	if {[lindex $treq 0] != $AuthTreq} {
		error "AuthError bad server"
	}
	set CHs [lindex $treq 3]

	# request ticket from authsrv
	set treq [lreplace $treq end-1 end $user $user]
	puts stderr "connecting to tcp!$authsrv!$authport"
	set asock [socket $authsrv $authport]
	fconfigure $asock -translation binary

	set ticket [getTicket $asock $sk1 $treq] ;# XXX catch
	set ctick [lindex $ticket 0]
	set stick [lindex $ticket 1]
	set num [lindex $ctick 0]
	set CHs2 [lindex $ctick 1]
	set cuid [lindex $ctick 2]
	set suid [lindex $ctick 3]
	set Kn [lindex $ctick 4]
	close $asock
	if {$num != $AuthTc || $CHs != $CHs2} {
		error "AuthError bad password for $user or bad auth server"
	} elseif {$num != $AuthTc} {
		error "AuthError bad password for $user"
	} elseif {$CHs != $CHs2} {
		error "bad auth server"
	}
	::9p::marshal::setKn $sk1 $Kn

	# Tattach
	::9p::marshal::setBuf $sk1 ""
	::9p::marshal::encTattach $sk1 [list $stick [list $AuthAc $CHs $gen]]
	set b [::9p::marshal::getBuf $sk1]
	::9p::write $cl $afid $b
	
	set a [::9p::read $cl $afid $AuthLen]
	::9p::marshal::setBuf $sk1 $a
	set r [::9p::marshal::decAuth $sk1]
	set num [lindex $r 0]
	set CHc2 [lindex $r 1]
	set gen2 [lindex $r 2]
	if {$num != $AuthAs || $CHc2 != $CHc} {
		# XXX check gen2 for replay
		error "AuthError bad server"
	}
	
	return
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.