You can use groupBy to keep the groups of tuples apart:
import Data.Function (on)
import Data.Ord (comparing)
import Data.List
possible_keys :: Ord k => [(k, [c])] -> [[c]]
possible_keys = concat . fmap sequenceA . fmap (fmap snd)
    . groupBy ((==) `on` fst) . sortBy (comparing fst)
(I'm assuming the input list isn't necessarily sorted. I'm sorting by the keys alone to make it minimally invasive. comparing fst is the same as compare `on` fst; unfortunately there isn't an analogous equating in the standard library yet. sequenceA is the same as sequence, only more general.)
This can be slimmed down a bit. The second functor law allows us to combine consecutive uses of fmap:
possible_keys :: Ord k => [(k, [c])] -> [[c]]
possible_keys = concat . fmap (sequenceA . fmap snd)
    . groupBy ((==) `on` fst) . sortBy (comparing fst)
fmap followed by sequenceA is traverse:
possible_keys :: Ord k => [(k, [c])] -> [[c]]
possible_keys = concat . fmap (traverse snd)
    . groupBy ((==) `on` fst) . sortBy (comparing fst)
Finally, fmap followed by concat on a list is concatMap (or (=<<), but that would be a little too cryptic here): 
possible_keys :: Ord k => [(k, [c])] -> [[c]]
possible_keys = concatMap (traverse snd)
    . groupBy ((==) `on` fst) . sortBy (comparing fst)
Note that this will generate strings of length one for keys with just one tuple -- sequenceA ["abc"] is ["a","b","c"]. If you don't want that, you can filter the groups of tuples immediately after the groupBy to get rid of those with just one tuple.